Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features

- 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))

Expand Down
2 changes: 2 additions & 0 deletions sentry-log4j2/api/sentry-log4j2.api
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ public class io/sentry/log4j2/SentryAppender : org/apache/logging/log4j/core/app
public static final field MECHANISM_TYPE Ljava/lang/String;
public fun <init> (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IScopes;[Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IScopes;[Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;ZLjava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IScopes;[Ljava/lang/String;)V
public fun append (Lorg/apache/logging/log4j/core/LogEvent;)V
protected fun captureLog (Lorg/apache/logging/log4j/core/LogEvent;)V
public static fun createAppender (Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;)Lio/sentry/log4j2/SentryAppender;
public static fun createAppender (Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/String;Ljava/lang/Boolean;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;)Lio/sentry/log4j2/SentryAppender;
protected fun createBreadcrumb (Lorg/apache/logging/log4j/core/LogEvent;)Lio/sentry/Breadcrumb;
protected fun createEvent (Lorg/apache/logging/log4j/core/LogEvent;)Lio/sentry/SentryEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class SentryAppender extends AbstractAppender {
private @NotNull Level minimumBreadcrumbLevel = Level.INFO;
private @NotNull Level minimumEventLevel = Level.ERROR;
private @NotNull Level minimumLevel = Level.INFO;
private final boolean enableLogs;
private final @Nullable Boolean debug;
private final @NotNull IScopes scopes;
private final @Nullable List<String> contextTags;
Expand Down Expand Up @@ -104,6 +105,32 @@ public SentryAppender(
final @Nullable ITransportFactory transportFactory,
final @NotNull IScopes scopes,
final @Nullable String[] contextTags) {
this(
name,
filter,
dsn,
minimumBreadcrumbLevel,
minimumEventLevel,
minimumLevel,
false,
debug,
transportFactory,
scopes,
contextTags);
}

public SentryAppender(
final @NotNull String name,
final @Nullable Filter filter,
final @Nullable String dsn,
final @Nullable Level minimumBreadcrumbLevel,
final @Nullable Level minimumEventLevel,
final @Nullable Level minimumLevel,
final boolean enableLogs,
final @Nullable Boolean debug,
final @Nullable ITransportFactory transportFactory,
final @NotNull IScopes scopes,
final @Nullable String[] contextTags) {
super(name, filter, null, true, null);
this.dsn = dsn;
if (minimumBreadcrumbLevel != null) {
Expand All @@ -115,6 +142,7 @@ public SentryAppender(
if (minimumLevel != null) {
this.minimumLevel = minimumLevel;
}
this.enableLogs = enableLogs;
this.debug = debug;
this.transportFactory = transportFactory;
this.scopes = scopes;
Expand All @@ -133,12 +161,34 @@ public SentryAppender(
* @param filter The filter, if any, to use.
* @return The SentryAppender.
*/
public static @Nullable SentryAppender createAppender(
final @Nullable String name,
final @Nullable Level minimumBreadcrumbLevel,
final @Nullable Level minimumEventLevel,
final @Nullable Level minimumLevel,
final @Nullable String dsn,
final @Nullable Boolean debug,
final @Nullable Filter filter,
final @Nullable String contextTags) {
return createAppender(
name,
minimumBreadcrumbLevel,
minimumEventLevel,
minimumLevel,
false,
dsn,
debug,
filter,
contextTags);
}

@PluginFactory
public static @Nullable SentryAppender createAppender(
@Nullable @PluginAttribute("name") final String name,
@Nullable @PluginAttribute("minimumBreadcrumbLevel") final Level minimumBreadcrumbLevel,
@Nullable @PluginAttribute("minimumEventLevel") final Level minimumEventLevel,
@Nullable @PluginAttribute("minimumLevel") final Level minimumLevel,
@Nullable @PluginAttribute("enableLogs") final Boolean enableLogs,
@Nullable @PluginAttribute("dsn") final String dsn,
@Nullable @PluginAttribute("debug") final Boolean debug,
@Nullable @PluginElement("filter") final Filter filter,
Expand All @@ -155,6 +205,7 @@ public SentryAppender(
minimumBreadcrumbLevel,
minimumEventLevel,
minimumLevel,
Boolean.TRUE.equals(enableLogs),
debug,
null,
ScopesAdapter.getInstance(),
Expand Down Expand Up @@ -206,7 +257,8 @@ void start(final @NotNull Sentry.OptionsConfiguration<SentryOptions> optionsConf

@Override
public void append(final @NotNull LogEvent eventObject) {
if (scopes.getOptions().getLogs().isEnabled()
if (enableLogs
&& scopes.getOptions().getLogs().isEnabled()
&& eventObject.getLevel().isMoreSpecificThan(minimumLevel)) {
captureLog(eventObject);
}
Expand Down
171 changes: 171 additions & 0 deletions sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.log4j2

import io.sentry.IScopes
import io.sentry.ITransportFactory
import io.sentry.InitPriority
import io.sentry.ScopesAdapter
Expand Down Expand Up @@ -28,14 +29,18 @@ import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.MarkerManager
import org.apache.logging.log4j.ThreadContext
import org.apache.logging.log4j.core.LogEvent
import org.apache.logging.log4j.core.LoggerContext
import org.apache.logging.log4j.core.config.AppenderRef
import org.apache.logging.log4j.core.config.Configuration
import org.apache.logging.log4j.core.config.LoggerConfig
import org.apache.logging.log4j.spi.ExtendedLogger
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

Expand All @@ -57,6 +62,8 @@ class SentryAppenderTest {
minimumLevel: Level? = null,
debug: Boolean? = null,
contextTags: List<String>? = null,
enableLogs: Boolean = true,
enableGlobalLogs: Boolean = true,
): ExtendedLogger {
if (transportFactory != null) {
this.transportFactory = transportFactory
Expand All @@ -71,6 +78,7 @@ class SentryAppenderTest {
minimumBreadcrumbLevel,
minimumEventLevel,
minimumLevel,
enableLogs,
debug,
this.transportFactory,
ScopesAdapter.getInstance(),
Expand Down Expand Up @@ -98,6 +106,7 @@ class SentryAppenderTest {

appender.start(
appender.getOptionsConfiguration { options ->
options.logs.isEnabled = enableGlobalLogs
options.logs.loggerBatchProcessorFactory =
ILoggerBatchProcessorFactory { options, client ->
LoggerBatchProcessor(options, client, ImmediateExecutorService())
Expand Down Expand Up @@ -253,6 +262,168 @@ class SentryAppenderTest {
.send(checkEvent { event -> assertEquals(SentryLevel.FATAL, event.level) }, anyOrNull())
}

@Test
fun `does not capture logs when local logs are disabled`() {
val logger = fixture.getSut(enableLogs = false, enableGlobalLogs = true)

logger.info("this should not be captured as a log")
Sentry.flush(10)

verify(fixture.transport, never()).send(checkLogs {})
}

@Test
fun `captures logs when local and aggregate logs are enabled`() {
val logger = fixture.getSut(enableLogs = true, enableGlobalLogs = true)

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 local logs are disabled`() {
val logger =
fixture.getSut(
minimumBreadcrumbLevel = Level.INFO,
minimumEventLevel = Level.ERROR,
enableLogs = false,
enableGlobalLogs = true,
)

logger.info("this should be a breadcrumb")
logger.error("this should be an event")
Sentry.flush(10)

verify(fixture.transport)
.send(
checkEvent { event ->
assertEquals("this should be an event", event.message?.formatted)
assertEquals("this should be a breadcrumb", event.breadcrumbs?.single()?.message)
},
anyOrNull(),
)
verify(fixture.transport, never()).send(checkLogs {})
}

@Test
fun `existing constructors default logs to disabled`() {
val scopes = mock<IScopes>()
val event = mock<LogEvent>()
whenever(event.level).thenReturn(Level.INFO)

val deprecatedAppender =
SentryAppender(
"deprecated",
null,
null,
Level.OFF,
Level.OFF,
null,
null,
scopes,
null,
)
val existingAppender =
SentryAppender(
"existing",
null,
null,
Level.OFF,
Level.OFF,
Level.INFO,
null,
null,
scopes,
null,
)

deprecatedAppender.append(event)
existingAppender.append(event)

verify(scopes, never()).logger()
}

@Test
fun `existing factory and plugin attribute default logs to disabled`() {
val event = mock<LogEvent>()
whenever(event.level).thenReturn(Level.INFO)
val existingAppender =
spy(
assertNotNull(
SentryAppender.createAppender(
"existing",
Level.OFF,
Level.OFF,
Level.INFO,
null,
null,
null,
null,
)
)
)
val pluginDefaultAppender =
spy(
assertNotNull(
SentryAppender.createAppender(
"plugin-default",
Level.OFF,
Level.OFF,
Level.INFO,
null,
null,
null,
null,
null,
)
)
)

existingAppender.append(event)
pluginDefaultAppender.append(event)

verify(existingAppender, never()).captureLog(event)
verify(pluginDefaultAppender, never()).captureLog(event)
}

@Test
fun `plugin attribute enables logs with explicit opt in`() {
initForTest {
it.dsn = "http://key@localhost/proj"
it.logs.isEnabled = true
}
val event = mock<LogEvent>()
whenever(event.level).thenReturn(Level.INFO)
val appender =
spy(
assertNotNull(
SentryAppender.createAppender(
"enabled",
Level.OFF,
Level.OFF,
Level.INFO,
true,
null,
null,
null,
null,
)
)
)
doNothing().whenever(appender).captureLog(event)

appender.append(event)

verify(appender).captureLog(event)
}

@Test
fun `converts trace log level to Sentry log level`() {
val logger = fixture.getSut(minimumLevel = Level.TRACE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
minimumBreadcrumbLevel="DEBUG"
minimumEventLevel="WARN"
minimumLevel="DEBUG"
enableLogs="true"
debug="true"
contextTags="userId,requestId"
/>
Expand Down
Loading