diff --git a/backend/src/main/java/org/raddatz/familienarchiv/config/SentryConfig.java b/backend/src/main/java/org/raddatz/familienarchiv/config/SentryConfig.java new file mode 100644 index 00000000..6b541385 --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/config/SentryConfig.java @@ -0,0 +1,39 @@ +package org.raddatz.familienarchiv.config; + +import io.sentry.Sentry; +import jakarta.annotation.PostConstruct; +import org.raddatz.familienarchiv.exception.DomainException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +// SentryAutoConfiguration is excluded (see application.yaml) because Spring Boot 4 / Spring +// Framework 7 cannot generate a bean name for the triply-nested +// SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration class. +// This bean replicates the essential init: DSN, environment, sample rate, PII guard, +// and DomainException filter. +@Configuration +public class SentryConfig { + + @Value("${sentry.dsn:}") + private String dsn; + + @Value("${sentry.environment:dev}") + private String environment; + + @Value("${sentry.traces-sample-rate:1.0}") + private double tracesSampleRate; + + @PostConstruct + public void init() { + if (dsn == null || dsn.isBlank()) { + return; + } + Sentry.init(options -> { + options.setDsn(dsn); + options.setEnvironment(environment); + options.setTracesSampleRate(tracesSampleRate); + options.setSendDefaultPii(false); + options.addIgnoredExceptionForType(DomainException.class); + }); + } +} diff --git a/backend/src/main/java/org/raddatz/familienarchiv/exception/GlobalExceptionHandler.java b/backend/src/main/java/org/raddatz/familienarchiv/exception/GlobalExceptionHandler.java index e99acfdd..aa33094d 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/exception/GlobalExceptionHandler.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/exception/GlobalExceptionHandler.java @@ -2,6 +2,7 @@ package org.raddatz.familienarchiv.exception; import java.util.stream.Collectors; +import io.sentry.Sentry; import jakarta.validation.ConstraintViolationException; import org.raddatz.familienarchiv.exception.DomainException; import org.raddatz.familienarchiv.exception.ErrorCode; @@ -63,6 +64,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity handleGeneric(Exception ex) { + Sentry.captureException(ex); log.error("Unhandled exception", ex); return ResponseEntity.internalServerError() .body(new ErrorResponse(ErrorCode.INTERNAL_ERROR, "An unexpected error occurred")); diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml index ead3d9e8..6bb21378 100644 --- a/backend/src/main/resources/application.yaml +++ b/backend/src/main/resources/application.yaml @@ -38,6 +38,13 @@ spring: starttls: enable: true + autoconfigure: + exclude: + # SentryAutoConfiguration fails on Spring Boot 4/Spring Framework 7: Spring cannot generate a + # bean name for the triply-nested SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration. + # Sentry is initialized manually via SentryConfig instead. See #580. + - io.sentry.spring.boot.jakarta.SentryAutoConfiguration + server: # Behind Caddy/reverse proxy: trust X-Forwarded-{Proto,For,Host} so that # request.getScheme(), redirect URLs, and Spring Session "Secure" cookies diff --git a/backend/src/test/resources/application-test.yaml b/backend/src/test/resources/application-test.yaml index e1a2f913..de2e9347 100644 --- a/backend/src/test/resources/application-test.yaml +++ b/backend/src/test/resources/application-test.yaml @@ -13,6 +13,12 @@ spring: password: test mail: host: localhost + autoconfigure: + exclude: + # SentryAutoConfiguration fails on Spring Boot 4/Spring Framework 7: Spring cannot generate a + # bean name for the triply-nested SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration. + # Sentry is wired manually via SentryConfig instead. See #580. + - io.sentry.spring.boot.jakarta.SentryAutoConfiguration # Disable OTel SDK entirely in tests — prevents auto-configuration from loading resource providers # (e.g. AzureAppServiceResourceProvider) that fail against the semconv version used here.