diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbfda10..2732c3b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
### Features
- add option to ignore source bundle upload failures ([#209](https://github.com/getsentry/sentry-maven-plugin/pull/209))
+- Autoinstall Profiler ([#222](https://github.com/getsentry/sentry-maven-plugin/pull/222))
### Dependencies
diff --git a/examples/sentry-maven-plugin-example/pom.xml b/examples/sentry-maven-plugin-example/pom.xml
index 68d8497..16d38b8 100644
--- a/examples/sentry-maven-plugin-example/pom.xml
+++ b/examples/sentry-maven-plugin-example/pom.xml
@@ -51,6 +51,7 @@
src/main/extra_param
+ false
diff --git a/examples/sentry-maven-plugin-example/src/main/java/io/sentry/samples/maven/SampleApplication.java b/examples/sentry-maven-plugin-example/src/main/java/io/sentry/samples/maven/SampleApplication.java
index ff0c091..67a3170 100644
--- a/examples/sentry-maven-plugin-example/src/main/java/io/sentry/samples/maven/SampleApplication.java
+++ b/examples/sentry-maven-plugin-example/src/main/java/io/sentry/samples/maven/SampleApplication.java
@@ -5,6 +5,7 @@
import io.sentry.Hint;
import io.sentry.ISpan;
import io.sentry.ITransaction;
+import io.sentry.ProfileLifecycle;
import io.sentry.Sentry;
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
@@ -83,6 +84,11 @@ public static void main(String[] args) throws InterruptedException {
// Set what percentage of traces should be collected
options.setTracesSampleRate(1.0); // set 0.5 to send 50% of traces
+ // Profiling configuration options
+ // Set ProfileLifecycle and ProfileSessionSampleRate
+ options.setProfileLifecycle(ProfileLifecycle.TRACE);
+ options.setProfileSessionSampleRate(1.0);
+
// Determine traces sample rate based on the sampling context
options.setTracesSampler(
context -> {
diff --git a/src/main/java/io/sentry/SentryInstallerLifecycleParticipant.java b/src/main/java/io/sentry/SentryInstallerLifecycleParticipant.java
index 5722532..4da1c73 100644
--- a/src/main/java/io/sentry/SentryInstallerLifecycleParticipant.java
+++ b/src/main/java/io/sentry/SentryInstallerLifecycleParticipant.java
@@ -6,6 +6,7 @@
import static io.sentry.autoinstall.jdbc.JdbcInstallStrategy.SENTRY_JDBC_ID;
import static io.sentry.autoinstall.log4j2.Log4j2InstallStrategy.SENTRY_LOG4J2_ID;
import static io.sentry.autoinstall.logback.LogbackInstallStrategy.SENTRY_LOGBACK_ID;
+import static io.sentry.autoinstall.profiler.ProfilerInstallStrategy.SENTRY_ASYNC_PROFILER_ID;
import static io.sentry.autoinstall.quartz.QuartzInstallStrategy.SENTRY_QUARTZ_ID;
import static io.sentry.autoinstall.spring.Spring5InstallStrategy.SENTRY_SPRING_5_ID;
import static io.sentry.autoinstall.spring.Spring6InstallStrategy.SENTRY_SPRING_6_ID;
@@ -22,6 +23,7 @@
import io.sentry.autoinstall.jdbc.JdbcInstallStrategy;
import io.sentry.autoinstall.log4j2.Log4j2InstallStrategy;
import io.sentry.autoinstall.logback.LogbackInstallStrategy;
+import io.sentry.autoinstall.profiler.ProfilerInstallStrategy;
import io.sentry.autoinstall.quartz.QuartzInstallStrategy;
import io.sentry.autoinstall.spring.*;
import io.sentry.config.ConfigParser;
@@ -64,7 +66,8 @@ public class SentryInstallerLifecycleParticipant extends AbstractMavenLifecycleP
GraphqlInstallStrategy.class,
Graphql22InstallStrategy.class,
JdbcInstallStrategy.class,
- QuartzInstallStrategy.class)
+ QuartzInstallStrategy.class,
+ ProfilerInstallStrategy.class)
.collect(Collectors.toList());
@Inject @NotNull ArtifactResolver resolver;
@@ -125,6 +128,7 @@ public void afterProjectsRead(final @NotNull MavenSession session)
autoInstallState.setInstallGraphql(shouldInstallGraphQL(resolvedArtifacts));
autoInstallState.setInstallJdbc(!isModuleAvailable(resolvedArtifacts, SENTRY_JDBC_ID));
autoInstallState.setInstallQuartz(!isModuleAvailable(resolvedArtifacts, SENTRY_QUARTZ_ID));
+ autoInstallState.setInstallProfiler(shouldInstallProfiler(resolvedArtifacts, pluginConfig));
for (final @NotNull Class extends AbstractIntegrationInstaller> installerClass :
installers) {
@@ -160,6 +164,12 @@ private boolean shouldInstallGraphQL(final @NotNull List resolvedArtif
|| isModuleAvailable(resolvedArtifacts, SENTRY_GRAPHQL_22_ID));
}
+ private boolean shouldInstallProfiler(
+ final @NotNull List resolvedArtifacts, final @NotNull PluginConfig pluginConfig) {
+ return pluginConfig.isInstallProfiler()
+ && !(isModuleAvailable(resolvedArtifacts, SENTRY_ASYNC_PROFILER_ID));
+ }
+
public static boolean isModuleAvailable(
final @NotNull List resolvedArtifacts, final @NotNull String artifactId) {
return resolvedArtifacts.stream()
diff --git a/src/main/java/io/sentry/autoinstall/AutoInstallState.java b/src/main/java/io/sentry/autoinstall/AutoInstallState.java
index 1b902b9..2744b56 100644
--- a/src/main/java/io/sentry/autoinstall/AutoInstallState.java
+++ b/src/main/java/io/sentry/autoinstall/AutoInstallState.java
@@ -12,6 +12,7 @@ public class AutoInstallState {
private boolean installGraphql = false;
private boolean installQuartz = false;
+ private boolean installProfiler = false;
public AutoInstallState(final @NotNull String sentryVersion) {
this.sentryVersion = sentryVersion;
@@ -72,4 +73,12 @@ public boolean isInstallQuartz() {
public void setInstallQuartz(boolean installQuartz) {
this.installQuartz = installQuartz;
}
+
+ public boolean isInstallProfiler() {
+ return installProfiler;
+ }
+
+ public void setInstallProfiler(boolean installProfiler) {
+ this.installProfiler = installProfiler;
+ }
}
diff --git a/src/main/java/io/sentry/autoinstall/profiler/ProfilerInstallStrategy.java b/src/main/java/io/sentry/autoinstall/profiler/ProfilerInstallStrategy.java
new file mode 100644
index 0000000..f8d34f3
--- /dev/null
+++ b/src/main/java/io/sentry/autoinstall/profiler/ProfilerInstallStrategy.java
@@ -0,0 +1,46 @@
+package io.sentry.autoinstall.profiler;
+
+import io.sentry.autoinstall.AbstractIntegrationInstaller;
+import io.sentry.autoinstall.AutoInstallState;
+import io.sentry.semver.Version;
+import java.util.List;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProfilerInstallStrategy extends AbstractIntegrationInstaller {
+ public static final @NotNull String SENTRY_ASYNC_PROFILER_ID = "sentry-async-profiler";
+
+ public ProfilerInstallStrategy() {
+ this(LoggerFactory.getLogger(ProfilerInstallStrategy.class));
+ }
+
+ public ProfilerInstallStrategy(final @NotNull Logger logger) {
+ super(logger);
+ }
+
+ @Override
+ protected @Nullable Artifact findThirdPartyDependency(
+ final @NotNull List resolvedArtifacts) {
+ // Return a dummy artifact as there are no third-party dependencies required
+ return new DefaultArtifact("dummy:dummy:1.0.0");
+ }
+
+ @Override
+ protected boolean shouldInstallModule(final @NotNull AutoInstallState autoInstallState) {
+ return autoInstallState.isInstallProfiler();
+ }
+
+ @Override
+ protected @NotNull Version minSupportedSentryVersion() {
+ return Version.create(8, 23, 0);
+ }
+
+ @Override
+ protected @NotNull String sentryModuleId() {
+ return SENTRY_ASYNC_PROFILER_ID;
+ }
+}
diff --git a/src/main/java/io/sentry/config/ConfigParser.java b/src/main/java/io/sentry/config/ConfigParser.java
index 3149edb..374b2a7 100644
--- a/src/main/java/io/sentry/config/ConfigParser.java
+++ b/src/main/java/io/sentry/config/ConfigParser.java
@@ -28,6 +28,7 @@ public class ConfigParser {
private static final @NotNull String AUTH_TOKEN_OPTION = "authToken";
private static final @NotNull String ADDITIONAL_SOURCE_DIRS_FOR_SOURCE_CONTEXT =
"additionalSourceDirsForSourceContext";
+ private static final @NotNull String INSTALL_PROFILER_FLAG = "installProfiler";
public @NotNull PluginConfig parseConfig(final @NotNull MavenProject project) {
final @NotNull PluginConfig pluginConfig = new PluginConfig();
@@ -101,6 +102,10 @@ public class ConfigParser {
: Arrays.stream(dom.getChild(ADDITIONAL_SOURCE_DIRS_FOR_SOURCE_CONTEXT).getChildren())
.map(Xpp3Dom::getValue)
.collect(Collectors.joining(",")));
+
+ pluginConfig.setInstallProfiler(
+ dom.getChild(INSTALL_PROFILER_FLAG) != null
+ && Boolean.parseBoolean(dom.getChild(INSTALL_PROFILER_FLAG).getValue()));
}
return pluginConfig;
diff --git a/src/main/java/io/sentry/config/PluginConfig.java b/src/main/java/io/sentry/config/PluginConfig.java
index 80619ef..0a1f496 100644
--- a/src/main/java/io/sentry/config/PluginConfig.java
+++ b/src/main/java/io/sentry/config/PluginConfig.java
@@ -36,6 +36,10 @@ public class PluginConfig {
private boolean skipValidateSdkDependencyVersions = DEFAULT_SKIP_VALIDATE_SDK_DEPENDENCY_VERSIONS;
private @NotNull String additionalSourceDirsForSourceContext =
DEFAULT_ADDITIONAL_SOURCE_DIRS_FOR_SOURCE_CONTEXT;
+ private boolean installProfiler = DEFAULT_INSTALL_PROFILER;
+
+ public static final boolean DEFAULT_INSTALL_PROFILER = false;
+ public static final @NotNull String DEFAULT_INSTALL_PROFILER_STRING = "false";
private @Nullable String org;
private @Nullable String project;
@@ -100,6 +104,10 @@ public void setSkipValidateSdkDependencyVersions(
this.skipValidateSdkDependencyVersions = skipValidateSdkDependencyVersions;
}
+ public void setInstallProfiler(final boolean installProfiler) {
+ this.installProfiler = installProfiler;
+ }
+
public boolean isSkipAutoInstall() {
return skipAutoInstall || skip;
}
@@ -124,6 +132,10 @@ public boolean isSkipValidateSdkDependencyVersions() {
return skipValidateSdkDependencyVersions;
}
+ public boolean isInstallProfiler() {
+ return installProfiler;
+ }
+
public @Nullable String getOrg() {
return org;
}
diff --git a/src/test/java/io/sentry/integration/autoinstall/profiler/ProfilerAutoInstallTestIT.kt b/src/test/java/io/sentry/integration/autoinstall/profiler/ProfilerAutoInstallTestIT.kt
new file mode 100644
index 0000000..cd63d8f
--- /dev/null
+++ b/src/test/java/io/sentry/integration/autoinstall/profiler/ProfilerAutoInstallTestIT.kt
@@ -0,0 +1,128 @@
+package io.sentry.integration.autoinstall.profiler
+
+import basePom
+import io.sentry.autoinstall.util.SdkVersionInfo
+import io.sentry.integration.installMavenWrapper
+import org.apache.maven.it.VerificationException
+import org.apache.maven.it.Verifier
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.io.TempDir
+import java.io.File
+import java.io.IOException
+import java.nio.file.Files
+import java.nio.file.StandardOpenOption
+import kotlin.io.path.Path
+
+class ProfilerAutoInstallTestIT {
+ @TempDir()
+ lateinit var file: File
+
+ @BeforeEach
+ fun installWrapper() {
+ installMavenWrapper(file, "3.8.6")
+ }
+
+ fun getPOM(
+ enableInstallProfilerFlag: Boolean = true,
+ profilerAlreadyInstalled: Boolean = false,
+ sentryProfilerVersion: String = SdkVersionInfo.getSentryVersion() ?: "8.23.0",
+ installedSentryVersion: String? = null,
+ ): String {
+ var dependencies = ""
+ var pluginConfiguration = ""
+
+ if (profilerAlreadyInstalled) {
+ dependencies =
+ """
+
+ io.sentry
+ sentry-async-profiler
+ $sentryProfilerVersion
+
+ """.trimIndent()
+ }
+
+ if (enableInstallProfilerFlag) {
+ pluginConfiguration =
+ """
+
+ true
+
+ """.trimIndent()
+ }
+
+ val pomContent = basePom(dependencies, installedSentryVersion, pluginConfiguration)
+
+ Files.write(Path("${file.absolutePath}/pom.xml"), pomContent.toByteArray(), StandardOpenOption.CREATE)
+
+ return file.absolutePath
+ }
+
+ @Test
+ @Throws(VerificationException::class, IOException::class)
+ fun `when installProfiler flag is false does not install`() {
+ val path = getPOM(enableInstallProfilerFlag = false)
+ val verifier = Verifier(path)
+ verifier.isAutoclean = false
+ verifier.executeGoal("install")
+ verifier.verifyFileNotPresent("target/lib/sentry-async-profiler-${SdkVersionInfo.getSentryVersion()}.jar")
+ verifier.resetStreams()
+ }
+
+ @Test
+ @Throws(VerificationException::class, IOException::class)
+ fun `when sentry-async-profiler is a direct dependency does not auto-install`() {
+ val path = getPOM(enableInstallProfilerFlag = true, profilerAlreadyInstalled = true)
+ val verifier = Verifier(path)
+ verifier.isAutoclean = false
+ verifier.executeGoal("install")
+ verifier.verifyTextInLog("sentry-async-profiler won't be installed because it was already installed directly")
+ verifier.verifyFilePresent("target/lib/sentry-async-profiler-${SdkVersionInfo.getSentryVersion()}.jar")
+ verifier.resetStreams()
+ }
+
+ @Test
+ @Throws(VerificationException::class, IOException::class)
+ fun `installs sentry-async-profiler with info message`() {
+ val path = getPOM()
+ val verifier = Verifier(path)
+ verifier.deleteDirectory("target")
+ verifier.isAutoclean = false
+ verifier.executeGoal("install")
+ verifier.verifyTextInLog("sentry-async-profiler was successfully installed with version: ${SdkVersionInfo.getSentryVersion()}")
+ verifier.verifyFilePresent("target/lib/sentry-async-profiler-${SdkVersionInfo.getSentryVersion()}.jar")
+ verifier.resetStreams()
+ verifier.deleteDirectory(path)
+ }
+
+ @Test
+ @Throws(VerificationException::class, IOException::class)
+ fun `auto-installed sentry-async-profiler version matches sentry version`() {
+ val sentryVersion = "8.23.0"
+ val path = getPOM(installedSentryVersion = sentryVersion)
+ val verifier = Verifier(path)
+ verifier.deleteDirectory("target")
+ verifier.isAutoclean = false
+ verifier.executeGoal("install")
+ verifier.verifyTextInLog("sentry-async-profiler was successfully installed with version: $sentryVersion")
+ verifier.verifyFilePresent("target/lib/sentry-async-profiler-$sentryVersion.jar")
+ verifier.resetStreams()
+ verifier.deleteDirectory(path)
+ }
+
+ @Test
+ @Throws(VerificationException::class, IOException::class)
+ fun `does not install when sentry version is too low`() {
+ val sentryVersion = "8.22.0"
+ val path = getPOM(installedSentryVersion = sentryVersion)
+ val verifier = Verifier(path)
+ verifier.deleteDirectory("target")
+ verifier.isAutoclean = false
+ verifier.executeGoal("install")
+ verifier.verifyTextInLog("sentry-async-profiler won't be installed because the current version")
+ verifier.verifyFileNotPresent("target/lib/sentry-async-profiler-$sentryVersion.jar")
+ verifier.resetStreams()
+ verifier.deleteDirectory(path)
+ }
+}
diff --git a/src/test/java/io/sentry/unit/autoinstall/profiler/ProfilerAutoInstallTest.kt b/src/test/java/io/sentry/unit/autoinstall/profiler/ProfilerAutoInstallTest.kt
new file mode 100644
index 0000000..fdfbe67
--- /dev/null
+++ b/src/test/java/io/sentry/unit/autoinstall/profiler/ProfilerAutoInstallTest.kt
@@ -0,0 +1,91 @@
+package io.sentry.unit.autoinstall.profiler
+
+import io.sentry.autoinstall.AutoInstallState
+import io.sentry.autoinstall.profiler.ProfilerInstallStrategy
+import io.sentry.unit.fakes.CapturingTestLogger
+import org.apache.maven.model.Dependency
+import org.eclipse.aether.artifact.Artifact
+import org.junit.jupiter.api.Test
+import org.slf4j.Logger
+import kotlin.test.assertTrue
+
+class ProfilerAutoInstallTest {
+ class Fixture {
+ val logger = CapturingTestLogger()
+ var dependencies = ArrayList()
+ val resolvedArtifacts = ArrayList()
+ lateinit var installState: AutoInstallState
+
+ fun getSut(
+ installProfiler: Boolean = true,
+ sentryVersion: String = "8.23.0",
+ ): ProfilerInstallStrategy {
+ dependencies = ArrayList()
+ installState = AutoInstallState(sentryVersion)
+ installState.isInstallProfiler = installProfiler
+
+ return ProfilerInstallStrategyImpl(logger)
+ }
+ }
+
+ private val fixture = Fixture()
+
+ @Test
+ fun `when installProfiler flag is false does not install`() {
+ val sut = fixture.getSut(installProfiler = false)
+
+ sut.install(fixture.dependencies, fixture.resolvedArtifacts, fixture.installState)
+
+ assertTrue {
+ fixture.logger.capturedMessage ==
+ "sentry-async-profiler won't be installed because it was already installed directly"
+ }
+
+ assertTrue(fixture.dependencies.none { it.groupId == "io.sentry" && it.artifactId == "sentry-async-profiler" })
+ }
+
+ @Test
+ fun `installs sentry-async-profiler with info message`() {
+ val sut = fixture.getSut()
+
+ sut.install(fixture.dependencies, fixture.resolvedArtifacts, fixture.installState)
+
+ assertTrue {
+ fixture.logger.capturedMessage ==
+ "sentry-async-profiler was successfully installed with version: 8.23.0"
+ }
+
+ assertTrue(fixture.dependencies.any { it.groupId == "io.sentry" && it.artifactId == "sentry-async-profiler" })
+ }
+
+ @Test
+ fun `when sentry version is too low logs message and does nothing`() {
+ val sut = fixture.getSut(sentryVersion = "8.22.0")
+
+ sut.install(fixture.dependencies, fixture.resolvedArtifacts, fixture.installState)
+
+ assertTrue {
+ fixture.logger.capturedMessage ==
+ "sentry-async-profiler won't be installed because the current version (8.22.0) is " +
+ "lower than the minimum supported sentry version 8.22.0"
+ }
+
+ assertTrue(fixture.dependencies.none { it.groupId == "io.sentry" && it.artifactId == "sentry-async-profiler" })
+ }
+
+ @Test
+ fun `installs with higher sentry version`() {
+ val sut = fixture.getSut(sentryVersion = "9.0.0")
+
+ sut.install(fixture.dependencies, fixture.resolvedArtifacts, fixture.installState)
+
+ assertTrue {
+ fixture.logger.capturedMessage ==
+ "sentry-async-profiler was successfully installed with version: 9.0.0"
+ }
+
+ assertTrue(fixture.dependencies.any { it.groupId == "io.sentry" && it.artifactId == "sentry-async-profiler" })
+ }
+
+ private class ProfilerInstallStrategyImpl(logger: Logger) : ProfilerInstallStrategy(logger)
+}