diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index 7a321474eac..13f438987a3 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -60,6 +60,9 @@ jobs: - sample: "sentry-samples-log4j2" agent: "false" agent-auto-init: "true" + - sample: "sentry-samples-jul" + agent: "false" + agent-auto-init: "true" steps: - uses: actions/checkout@v4 with: diff --git a/sentry-samples/sentry-samples-jul/build.gradle.kts b/sentry-samples/sentry-samples-jul/build.gradle.kts index 41ba015e555..377baa612f5 100644 --- a/sentry-samples/sentry-samples-jul/build.gradle.kts +++ b/sentry-samples/sentry-samples-jul/build.gradle.kts @@ -1,17 +1,80 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { java application + kotlin("jvm") alias(libs.plugins.gradle.versions) + id("com.github.johnrengelman.shadow") version "8.1.1" } application { mainClass.set("io.sentry.samples.jul.Main") } +java.sourceCompatibility = JavaVersion.VERSION_17 + +java.targetCompatibility = JavaVersion.VERSION_17 + +repositories { mavenCentral() } + configure { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +tasks.withType().configureEach { + kotlinOptions { + freeCompilerArgs = listOf("-Xjsr305=strict") + jvmTarget = JavaVersion.VERSION_17.toString() + } } dependencies { implementation(projects.sentryJul) implementation(libs.slf4j.jdk14) + + testImplementation(kotlin(Config.kotlinStdLib)) + testImplementation(projects.sentry) + testImplementation(projects.sentrySystemTestSupport) + testImplementation(libs.kotlin.test.junit) + testImplementation(libs.slf4j.api) + testImplementation(libs.slf4j.jdk14) +} + +// Configure the Shadow JAR (executable JAR with all dependencies) +tasks.shadowJar { + manifest { attributes["Main-Class"] = "io.sentry.samples.jul.Main" } + archiveClassifier.set("") // Remove the classifier so it replaces the regular JAR + mergeServiceFiles() +} + +// Make the regular jar task depend on shadowJar +tasks.jar { + enabled = false + dependsOn(tasks.shadowJar) +} + +// Fix the startScripts task dependency +tasks.startScripts { dependsOn(tasks.shadowJar) } + +configure { test { java.srcDir("src/test/java") } } + +tasks.register("systemTest").configure { + group = "verification" + description = "Runs the System tests" + + outputs.upToDateWhen { false } + + maxParallelForks = 1 + + // Cap JVM args per test + minHeapSize = "128m" + maxHeapSize = "1g" + + filter { includeTestsMatching("io.sentry.systemtest*") } +} + +tasks.named("test").configure { + require(this is Test) + + filter { excludeTestsMatching("io.sentry.systemtest.*") } } diff --git a/sentry-samples/sentry-samples-jul/src/main/resources/logging.properties b/sentry-samples/sentry-samples-jul/src/main/resources/logging.properties index df0debb9b44..0bdab173235 100644 --- a/sentry-samples/sentry-samples-jul/src/main/resources/logging.properties +++ b/sentry-samples/sentry-samples-jul/src/main/resources/logging.properties @@ -1,7 +1,9 @@ -io.sentry.jul.SentryHandler.minimumEventLevel=FINE +io.sentry.jul.SentryHandler.minimumEventLevel=INFO io.sentry.jul.SentryHandler.minimumBreadcrumbLevel=CONFIG -io.sentry.jul.SentryHandler.minimumLevel=FINE +io.sentry.jul.SentryHandler.minimumLevel=INFO io.sentry.jul.SentryHandler.printfStyle=true -io.sentry.jul.SentryHandler.level=CONFIG +io.sentry.jul.SentryHandler.level=FINEST +java.util.logging.ConsoleHandler.level = FINE handlers=io.sentry.jul.SentryHandler -level=CONFIG +level=FINEST +io.sentry.samples.jul.Main.level=FINEST diff --git a/sentry-samples/sentry-samples-jul/src/test/kotlin/io/sentry/DummyTest.kt b/sentry-samples/sentry-samples-jul/src/test/kotlin/io/sentry/DummyTest.kt new file mode 100644 index 00000000000..6f762b7e453 --- /dev/null +++ b/sentry-samples/sentry-samples-jul/src/test/kotlin/io/sentry/DummyTest.kt @@ -0,0 +1,12 @@ +package io.sentry + +import kotlin.test.Test +import kotlin.test.assertTrue + +class DummyTest { + @Test + fun `the only test`() { + // only needed to have more than 0 tests and not fail the build + assertTrue(true) + } +} diff --git a/sentry-samples/sentry-samples-jul/src/test/kotlin/io/sentry/systemtest/ConsoleApplicationSystemTest.kt b/sentry-samples/sentry-samples-jul/src/test/kotlin/io/sentry/systemtest/ConsoleApplicationSystemTest.kt new file mode 100644 index 00000000000..3c89c5a7e20 --- /dev/null +++ b/sentry-samples/sentry-samples-jul/src/test/kotlin/io/sentry/systemtest/ConsoleApplicationSystemTest.kt @@ -0,0 +1,65 @@ +package io.sentry.systemtest + +import io.sentry.SentryLevel +import io.sentry.systemtest.util.TestHelper +import java.util.concurrent.TimeUnit +import org.junit.Assert +import org.junit.Before +import org.junit.Test + +class ConsoleApplicationSystemTest { + lateinit var testHelper: TestHelper + + @Before + fun setup() { + testHelper = TestHelper("http://localhost:8000") + testHelper.reset() + } + + @Test + fun `jul application sends expected events when run as JAR`() { + val jarFile = testHelper.findJar("sentry-samples-jul") + val process = + testHelper.launch( + jarFile, + mapOf( + "SENTRY_DSN" to testHelper.dsn, + "SENTRY_TRACES_SAMPLE_RATE" to "1.0", + "SENTRY_ENABLE_PRETTY_SERIALIZATION_OUTPUT" to "false", + "SENTRY_DEBUG" to "true", + ), + ) + + process.waitFor(30, TimeUnit.SECONDS) + Assert.assertEquals(0, process.exitValue()) + + // Verify that we received the expected events + verifyExpectedEvents() + } + + private fun verifyExpectedEvents() { + // Verify we received the RuntimeException + testHelper.ensureErrorReceived { event -> + event.exceptions?.any { ex -> + ex.type == "RuntimeException" && ex.value == "Invalid productId=445" + } == true && event.message?.message == "Something went wrong" && event.level?.name == "ERROR" + } + + testHelper.ensureErrorReceived { event -> + event.breadcrumbs?.firstOrNull { + it.message == "Hello Sentry!" && it.level == SentryLevel.DEBUG + } != null + } + + testHelper.ensureErrorReceived { event -> + event.breadcrumbs?.firstOrNull { + it.message == "User has made a purchase of product: 445" && it.level == SentryLevel.INFO + } != null + } + + testHelper.ensureLogsReceived { logs, _ -> + testHelper.doesContainLogWithBody(logs, "User has made a purchase of product: 445") && + testHelper.doesContainLogWithBody(logs, "Something went wrong") + } + } +} diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 09bf8cd1ec7..7269f8d8955 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -616,6 +616,7 @@ def get_available_modules(self) -> List[ModuleConfig]: ModuleConfig("sentry-samples-console-opentelemetry-noagent", "false", "true", "false"), ModuleConfig("sentry-samples-logback", "false", "true", "false"), ModuleConfig("sentry-samples-log4j2", "false", "true", "false"), + ModuleConfig("sentry-samples-jul", "false", "true", "false"), ] def _find_module_number(self, module_name: str, agent: str, auto_init: str) -> int: