Skip to content

Commit

Permalink
Merge 6babb27 into 8d29d97
Browse files Browse the repository at this point in the history
  • Loading branch information
adinauer committed Sep 27, 2023
2 parents 8d29d97 + 6babb27 commit bc5c321
Show file tree
Hide file tree
Showing 67 changed files with 1,552 additions and 33 deletions.
1 change: 1 addition & 0 deletions .craft.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ targets:
maven:io.sentry:sentry-apollo:
maven:io.sentry:sentry-jdbc:
maven:io.sentry:sentry-graphql:
# maven:io.sentry:sentry-quartz:
maven:io.sentry:sentry-android-navigation:
maven:io.sentry:sentry-compose:
maven:io.sentry:sentry-compose-android:
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report_java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ body:
- sentry-logback
- sentry-log4j2
- sentry-graphql
- sentry-quartz
- sentry-openfeign
- sentry-apache-http-client-5
- other
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@

- Add `sendModules` option for disable sending modules ([#2926](https://github.com/getsentry/sentry-java/pull/2926))
- Send `db.system` and `db.name` in span data for androidx.sqlite spans ([#2928](https://github.com/getsentry/sentry-java/pull/2928))
- Add API for sending checkins (CRONS) manually ([#2935](https://github.com/getsentry/sentry-java/pull/2935))
- Check-ins (CRONS) support ([#2952](https://github.com/getsentry/sentry-java/pull/2952))
- Add API for sending check-ins (CRONS) manually ([#2935](https://github.com/getsentry/sentry-java/pull/2935))
- Support check-ins (CRONS) for Quartz ([#2940](https://github.com/getsentry/sentry-java/pull/2940))
- `@SentryCheckIn` annotation and advice config for Spring ([#2946](https://github.com/getsentry/sentry-java/pull/2946))
- Add option for ignoring certain monitor slugs ([#2943](https://github.com/getsentry/sentry-java/pull/2943))

### Fixes

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Sentry SDK for Java and Android
| sentry-log4j2 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-log4j2/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-log4j2) |
| sentry-bom | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-bom/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-bom) |
| sentry-graphql | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-graphql/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-graphql) |
| sentry-quartz | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-quartz/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-quartz) |
| sentry-openfeign | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-openfeign/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-openfeign) |
| sentry-opentelemetry-agent | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-opentelemetry-agent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-opentelemetry-agent) |
| sentry-opentelemetry-agentcustomization | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-opentelemetry-agentcustomization/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-opentelemetry-agentcustomization) |
Expand Down
5 changes: 5 additions & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ object Config {

val springBootStarter = "org.springframework.boot:spring-boot-starter:$springBootVersion"
val springBootStarterGraphql = "org.springframework.boot:spring-boot-starter-graphql:$springBootVersion"
val springBootStarterQuartz = "org.springframework.boot:spring-boot-starter-quartz:$springBootVersion"
val springBootStarterTest = "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
val springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
val springBootStarterWebsocket = "org.springframework.boot:spring-boot-starter-websocket:$springBootVersion"
Expand All @@ -85,6 +86,7 @@ object Config {

val springBoot3Starter = "org.springframework.boot:spring-boot-starter:$springBoot3Version"
val springBoot3StarterGraphql = "org.springframework.boot:spring-boot-starter-graphql:$springBoot3Version"
val springBoot3StarterQuartz = "org.springframework.boot:spring-boot-starter-quartz:$springBoot3Version"
val springBoot3StarterTest = "org.springframework.boot:spring-boot-starter-test:$springBoot3Version"
val springBoot3StarterWeb = "org.springframework.boot:spring-boot-starter-web:$springBoot3Version"
val springBoot3StarterWebsocket = "org.springframework.boot:spring-boot-starter-websocket:$springBoot3Version"
Expand Down Expand Up @@ -128,6 +130,8 @@ object Config {

val graphQlJava = "com.graphql-java:graphql-java:17.3"

val quartz = "org.quartz-scheduler:quartz:2.3.0"

val kotlinReflect = "org.jetbrains.kotlin:kotlin-reflect"
val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib"

Expand Down Expand Up @@ -227,6 +231,7 @@ object Config {
val SENTRY_APOLLO3_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo3"
val SENTRY_APOLLO_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo"
val SENTRY_GRAPHQL_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.graphql"
val SENTRY_QUARTZ_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.quartz"
val SENTRY_JDBC_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.jdbc"
val SENTRY_SERVLET_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet"
val SENTRY_SERVLET_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet.jakarta"
Expand Down
16 changes: 16 additions & 0 deletions sentry-quartz/api/sentry-quartz.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
public final class io/sentry/quartz/BuildConfig {
public static final field SENTRY_QUARTZ_SDK_NAME Ljava/lang/String;
public static final field VERSION_NAME Ljava/lang/String;
}

public final class io/sentry/quartz/SentryJobListener : org/quartz/JobListener {
public static final field SENTRY_CHECK_IN_ID_KEY Ljava/lang/String;
public static final field SENTRY_SLUG_KEY Ljava/lang/String;
public fun <init> ()V
public fun <init> (Lio/sentry/IHub;)V
public fun getName ()Ljava/lang/String;
public fun jobExecutionVetoed (Lorg/quartz/JobExecutionContext;)V
public fun jobToBeExecuted (Lorg/quartz/JobExecutionContext;)V
public fun jobWasExecuted (Lorg/quartz/JobExecutionContext;Lorg/quartz/JobExecutionException;)V
}

83 changes: 83 additions & 0 deletions sentry-quartz/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import net.ltgt.gradle.errorprone.errorprone
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
`java-library`
kotlin("jvm")
jacoco
id(Config.QualityPlugins.errorProne)
id(Config.QualityPlugins.gradleVersions)
id(Config.BuildPlugins.buildConfig) version Config.BuildPlugins.buildConfigVersion
}

configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString()
kotlinOptions.languageVersion = Config.kotlinCompatibleLanguageVersion
}

dependencies {
api(projects.sentry)
compileOnly(Config.Libs.quartz)

compileOnly(Config.CompileOnly.nopen)
errorprone(Config.CompileOnly.nopenChecker)
errorprone(Config.CompileOnly.errorprone)
errorprone(Config.CompileOnly.errorProneNullAway)
compileOnly(Config.CompileOnly.jetbrainsAnnotations)

// tests
testImplementation(projects.sentry)
testImplementation(projects.sentryTestSupport)
testImplementation(kotlin(Config.kotlinStdLib))
testImplementation(Config.TestLibs.kotlinTestJunit)
testImplementation(Config.TestLibs.mockitoKotlin)
testImplementation(Config.TestLibs.mockitoInline)
}

configure<SourceSetContainer> {
test {
java.srcDir("src/test/java")
}
}

jacoco {
toolVersion = Config.QualityPlugins.Jacoco.version
}

tasks.jacocoTestReport {
reports {
xml.required.set(true)
html.required.set(false)
}
}

tasks {
jacocoTestCoverageVerification {
violationRules {
rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } }
}
}
check {
dependsOn(jacocoTestCoverageVerification)
dependsOn(jacocoTestReport)
}
}

tasks.withType<JavaCompile>().configureEach {
options.errorprone {
check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR)
option("NullAway:AnnotatedPackages", "io.sentry")
}
}

buildConfig {
useJavaOutput()
packageName("io.sentry.quartz")
buildConfigField("String", "SENTRY_QUARTZ_SDK_NAME", "\"${Config.Sentry.SENTRY_QUARTZ_SDK_NAME}\"")
buildConfigField("String", "VERSION_NAME", "\"${project.version}\"")
}
102 changes: 102 additions & 0 deletions sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package io.sentry.quartz;

import io.sentry.BuildConfig;
import io.sentry.CheckIn;
import io.sentry.CheckInStatus;
import io.sentry.HubAdapter;
import io.sentry.IHub;
import io.sentry.SentryIntegrationPackageStorage;
import io.sentry.SentryLevel;
import io.sentry.protocol.SentryId;
import io.sentry.util.Objects;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;

@ApiStatus.Experimental
public final class SentryJobListener implements JobListener {

public static final String SENTRY_CHECK_IN_ID_KEY = "sentry-checkin-id";
public static final String SENTRY_SLUG_KEY = "sentry-slug";

private final @NotNull IHub hub;

public SentryJobListener() {
this(HubAdapter.getInstance());
}

public SentryJobListener(final @NotNull IHub hub) {
this.hub = Objects.requireNonNull(hub, "hub is required");
SentryIntegrationPackageStorage.getInstance().addIntegration("Quartz");
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-quartz", BuildConfig.VERSION_NAME);
}

@Override
public String getName() {
return "sentry-job-listener";
}

@Override
public void jobToBeExecuted(final @NotNull JobExecutionContext context) {
try {
final @Nullable String maybeSlug = getSlug(context);
if (maybeSlug == null) {
return;
}
final @NotNull String slug = maybeSlug;
final @NotNull CheckIn checkIn = new CheckIn(slug, CheckInStatus.IN_PROGRESS);
final @NotNull SentryId checkInId = hub.captureCheckIn(checkIn);
context.put(SENTRY_CHECK_IN_ID_KEY, checkInId);
context.put(SENTRY_SLUG_KEY, slug);
} catch (Throwable t) {
hub.getOptions()
.getLogger()
.log(SentryLevel.ERROR, "Unable to capture check-in in jobToBeExecuted.", t);
}
}

private @Nullable String getSlug(final @NotNull JobExecutionContext context) {
final @Nullable JobDataMap jobDataMap = context.getMergedJobDataMap();
if (jobDataMap != null) {
final @Nullable Object o = jobDataMap.get(SENTRY_SLUG_KEY);
if (o != null) {
return o.toString();
}
}

return null;
}

@Override
public void jobExecutionVetoed(JobExecutionContext context) {
// do nothing
}

@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
try {
final @Nullable Object checkInIdObjectFromContext = context.get(SENTRY_CHECK_IN_ID_KEY);
final @Nullable Object slugObjectFromContext = context.get(SENTRY_SLUG_KEY);
final @NotNull SentryId checkInId =
checkInIdObjectFromContext == null
? new SentryId()
: (SentryId) checkInIdObjectFromContext;
final @Nullable String slug =
slugObjectFromContext == null ? null : (String) slugObjectFromContext;
if (slug != null) {
final boolean isFailed = jobException != null;
final @NotNull CheckInStatus status = isFailed ? CheckInStatus.ERROR : CheckInStatus.OK;
hub.captureCheckIn(new CheckIn(checkInId, slug, status));
}
} catch (Throwable t) {
hub.getOptions()
.getLogger()
.log(SentryLevel.ERROR, "Unable to capture check-in in jobWasExecuted.", t);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies {
implementation(Config.Libs.springBoot3StarterWeb)
implementation(Config.Libs.springBoot3StarterWebsocket)
implementation(Config.Libs.springBoot3StarterGraphql)
implementation(Config.Libs.springBoot3StarterQuartz)
implementation(Config.Libs.springBoot3StarterWebflux)
implementation(Config.Libs.springBoot3StarterAop)
implementation(Config.Libs.aspectj)
Expand All @@ -32,6 +33,7 @@ dependencies {
implementation(projects.sentrySpringBootStarterJakarta)
implementation(projects.sentryLogback)
implementation(projects.sentryGraphql)
implementation(projects.sentryQuartz)

// database query tracing
implementation(projects.sentryJdbc)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
package io.sentry.samples.spring.boot.jakarta;

import io.sentry.CheckIn;
import io.sentry.CheckInStatus;
import io.sentry.DateUtils;
import io.sentry.Sentry;
import io.sentry.protocol.SentryId;
import io.sentry.spring.jakarta.checkin.SentryCheckIn;
import io.sentry.spring.jakarta.tracing.SentryTransaction;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
Expand All @@ -22,24 +17,10 @@ public class CustomJob {

private static final Logger LOGGER = LoggerFactory.getLogger(CustomJob.class);

@SentryCheckIn("monitor_slug_1")
@Scheduled(fixedRate = 3 * 60 * 1000L)
void execute() throws InterruptedException {
final @NotNull SentryId checkInId =
Sentry.captureCheckIn(new CheckIn("my_monitor_slug", CheckInStatus.IN_PROGRESS));
final long startTime = System.currentTimeMillis();
boolean didError = false;
try {
LOGGER.info("Executing scheduled job");
Thread.sleep(2000L);
Sentry.captureCheckIn(new CheckIn(checkInId, "my_monitor_slug", CheckInStatus.OK));
} catch (Throwable t) {
didError = true;
throw t;
} finally {
final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK;
CheckIn checkIn = new CheckIn(checkInId, "my_monitor_slug", status);
checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime));
Sentry.captureCheckIn(checkIn);
}
LOGGER.info("Executing scheduled job");
Thread.sleep(2000L);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package io.sentry.samples.spring.boot.jakarta;

import static io.sentry.quartz.SentryJobListener.SENTRY_SLUG_KEY;

import io.sentry.samples.spring.boot.jakarta.quartz.SampleJob;
import java.util.Collections;
import org.quartz.JobDetail;
import org.quartz.SimpleTrigger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;

Expand All @@ -24,4 +33,33 @@ RestTemplate restTemplate(RestTemplateBuilder builder) {
WebClient webClient(WebClient.Builder builder) {
return builder.build();
}

@Bean
public JobDetailFactoryBean jobDetail() {
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
jobDetailFactory.setJobClass(SampleJob.class);
jobDetailFactory.setDurability(true);
jobDetailFactory.setJobDataAsMap(
Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_job_detail"));
return jobDetailFactory;
}

@Bean
public SimpleTriggerFactoryBean trigger(JobDetail job) {
SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
trigger.setJobDetail(job);
trigger.setRepeatInterval(2 * 60 * 1000); // every two minutes
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setJobDataAsMap(
Collections.singletonMap(SENTRY_SLUG_KEY, "monitor_slug_simple_trigger"));
return trigger;
}

@Bean
public CronTriggerFactoryBean cronTrigger(JobDetail job) {
CronTriggerFactoryBean trigger = new CronTriggerFactoryBean();
trigger.setJobDetail(job);
trigger.setCronExpression("0 0/5 * ? * *"); // every five minutes
return trigger;
}
}

0 comments on commit bc5c321

Please sign in to comment.