diff --git a/CHANGELOG.md b/CHANGELOG.md index b113faacfef..7af676f676a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Add an explicit Logs opt-in to the JUL handler ([#5942](https://github.com/getsentry/sentry-java/pull/5942)) - Add an explicit Logs opt-in to the Log4j2 appender ([#5941](https://github.com/getsentry/sentry-java/pull/5941)) - Add an explicit Logs opt-in to the Logback appender ([#5940](https://github.com/getsentry/sentry-java/pull/5940)) - Make `ISpan.startChild` overloads with `SpanOptions` public ([#5927](https://github.com/getsentry/sentry-java/pull/5927)) diff --git a/sentry-jul/api/sentry-jul.api b/sentry-jul/api/sentry-jul.api index 7a7f6a26d32..1fdf4895971 100644 --- a/sentry-jul/api/sentry-jul.api +++ b/sentry-jul/api/sentry-jul.api @@ -15,8 +15,10 @@ public class io/sentry/jul/SentryHandler : java/util/logging/Handler { public fun getMinimumBreadcrumbLevel ()Ljava/util/logging/Level; public fun getMinimumEventLevel ()Ljava/util/logging/Level; public fun getMinimumLevel ()Ljava/util/logging/Level; + public fun isEnableLogs ()Z public fun isPrintfStyle ()Z public fun publish (Ljava/util/logging/LogRecord;)V + public fun setEnableLogs (Z)V public fun setMinimumBreadcrumbLevel (Ljava/util/logging/Level;)V public fun setMinimumEventLevel (Ljava/util/logging/Level;)V public fun setMinimumLevel (Ljava/util/logging/Level;)V diff --git a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java index 4442052dd41..37ce146d3c6 100644 --- a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java +++ b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java @@ -53,6 +53,8 @@ public class SentryHandler extends Handler { */ private boolean printfStyle; + private boolean enableLogs; + private @NotNull Level minimumBreadcrumbLevel = Level.INFO; private @NotNull Level minimumEventLevel = Level.SEVERE; private @NotNull Level minimumLevel = Level.INFO; @@ -112,7 +114,8 @@ public void publish(final @NotNull LogRecord record) { return; } try { - if (ScopesAdapter.getInstance().getOptions().getLogs().isEnabled() + if (enableLogs + && ScopesAdapter.getInstance().getOptions().getLogs().isEnabled() && record.getLevel().intValue() >= minimumLevel.intValue()) { captureLog(record); } @@ -191,6 +194,7 @@ private void retrieveProperties() { final LogManager manager = LogManager.getLogManager(); final String className = SentryHandler.class.getName(); setPrintfStyle(Boolean.parseBoolean(manager.getProperty(className + ".printfStyle"))); + setEnableLogs(Boolean.parseBoolean(manager.getProperty(className + ".enableLogs"))); setLevel(parseLevelOrDefault(manager.getProperty(className + ".level"))); final String minimumBreadCrumbLevel = manager.getProperty(className + ".minimumBreadcrumbLevel"); @@ -390,6 +394,14 @@ public void setPrintfStyle(final boolean printfStyle) { this.printfStyle = printfStyle; } + public void setEnableLogs(final boolean enableLogs) { + this.enableLogs = enableLogs; + } + + public boolean isEnableLogs() { + return enableLogs; + } + public void setMinimumBreadcrumbLevel(final @Nullable Level minimumBreadcrumbLevel) { if (minimumBreadcrumbLevel != null) { this.minimumBreadcrumbLevel = minimumBreadcrumbLevel; diff --git a/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt b/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt index ab160faac40..a169c710b0a 100644 --- a/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt +++ b/sentry-jul/src/test/kotlin/io/sentry/jul/SentryHandlerTest.kt @@ -28,6 +28,7 @@ import kotlin.test.assertNull import kotlin.test.assertTrue import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.mock +import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.slf4j.MDC @@ -40,6 +41,8 @@ class SentryHandlerTest { val transport: ITransport = mock(), contextTags: List? = null, printfStyle: Boolean? = null, + enableLogs: Boolean? = true, + enableGlobalLogs: Boolean = true, ) { var logger: Logger var handler: SentryHandler @@ -48,6 +51,7 @@ class SentryHandlerTest { val options = SentryOptions() options.dsn = "http://key@localhost/proj" options.setTransportFactory { _, _ -> transport } + options.logs.isEnabled = enableGlobalLogs options.logs.loggerBatchProcessorFactory = ILoggerBatchProcessorFactory { options, client -> LoggerBatchProcessor(options, client, ImmediateExecutorService()) } @@ -58,6 +62,9 @@ class SentryHandlerTest { handler.setMinimumBreadcrumbLevel(minimumBreadcrumbLevel) handler.setMinimumEventLevel(minimumEventLevel) handler.setMinimumLevel(minimumLevel) + if (enableLogs != null) { + handler.setEnableLogs(enableLogs) + } if (printfStyle == true) { handler.setPrintfStyle(printfStyle) } @@ -318,11 +325,22 @@ class SentryHandlerTest { @Test fun `fetches configuration from logging dot properties`() { - fixture = Fixture(configureWithLogManager = true) + fixture = Fixture(configureWithLogManager = true, enableLogs = null) assertEquals(Level.CONFIG, fixture.handler.minimumBreadcrumbLevel) assertEquals(Level.WARNING, fixture.handler.minimumEventLevel) assertEquals(Level.ALL, fixture.handler.level) assertTrue(fixture.handler.isPrintfStyle) + assertTrue(fixture.handler.isEnableLogs) + + fixture.logger.info("this should be captured as a log") + Sentry.flush(10) + + verify(fixture.transport) + .send( + checkLogs { logs -> + assertEquals("this should be captured as a log", logs.items.first().body) + } + ) } @Test @@ -418,6 +436,58 @@ class SentryHandlerTest { ) } + @Test + fun `does not capture logs by default`() { + fixture = Fixture(enableLogs = null, enableGlobalLogs = true) + + assertFalse(fixture.handler.isEnableLogs) + fixture.logger.info("this should not be captured as a log") + Sentry.flush(10) + + verify(fixture.transport, never()).send(checkLogs {}) + } + + @Test + fun `captures logs when enabled through Java`() { + fixture = Fixture(enableLogs = true, enableGlobalLogs = true) + + assertTrue(fixture.handler.isEnableLogs) + fixture.logger.info("this should be captured as a log") + Sentry.flush(10) + + verify(fixture.transport) + .send( + checkLogs { logs -> + assertEquals("this should be captured as a log", logs.items.first().body) + } + ) + } + + @Test + fun `captures events and breadcrumbs when logs are disabled`() { + fixture = + Fixture( + minimumBreadcrumbLevel = Level.INFO, + minimumEventLevel = Level.SEVERE, + enableLogs = false, + enableGlobalLogs = true, + ) + + fixture.logger.info("this should be a breadcrumb") + fixture.logger.severe("this should be an event") + Sentry.flush(10) + + verify(fixture.transport) + .send( + checkEvent { event -> + assertEquals("this should be an event", event.message?.message) + assertEquals("this should be a breadcrumb", event.breadcrumbs?.single()?.message) + }, + anyOrNull(), + ) + verify(fixture.transport, never()).send(checkLogs {}) + } + @Test fun `converts finest log level to Sentry log level`() { fixture = Fixture(minimumLevel = Level.FINEST) diff --git a/sentry-jul/src/test/resources/logging.properties b/sentry-jul/src/test/resources/logging.properties index 9ac994b722a..25ac65e1f66 100644 --- a/sentry-jul/src/test/resources/logging.properties +++ b/sentry-jul/src/test/resources/logging.properties @@ -3,5 +3,6 @@ io.sentry.jul.SentryHandler.minimumEventLevel=WARNING io.sentry.jul.SentryHandler.minimumBreadcrumbLevel=CONFIG io.sentry.jul.SentryHandler.minimumLevel=CONFIG io.sentry.jul.SentryHandler.printfStyle=true +io.sentry.jul.SentryHandler.enableLogs=true jul.SentryHandlerTest.handlers=java.util.logging.ConsoleHandler, io.sentry.jul.SentryHandler 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 0bdab173235..db3026bfb9f 100644 --- a/sentry-samples/sentry-samples-jul/src/main/resources/logging.properties +++ b/sentry-samples/sentry-samples-jul/src/main/resources/logging.properties @@ -2,6 +2,7 @@ io.sentry.jul.SentryHandler.minimumEventLevel=INFO io.sentry.jul.SentryHandler.minimumBreadcrumbLevel=CONFIG io.sentry.jul.SentryHandler.minimumLevel=INFO io.sentry.jul.SentryHandler.printfStyle=true +io.sentry.jul.SentryHandler.enableLogs=true io.sentry.jul.SentryHandler.level=FINEST java.util.logging.ConsoleHandler.level = FINE handlers=io.sentry.jul.SentryHandler