Skip to content

Add options for Logs #4374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: feat/log-rate-limit
Choose a base branch
from
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import io.sentry.ProfilingTraceData
import io.sentry.Sentry
import io.sentry.SentryEnvelope
import io.sentry.SentryEvent
import io.sentry.SentryLogEvents
import io.sentry.SentryLogEvent
import io.sentry.SentryOptions
import io.sentry.SentryReplayEvent
import io.sentry.Session
Expand Down Expand Up @@ -186,7 +186,7 @@ class SessionTrackingIntegrationTest {
TODO("Not yet implemented")
}

override fun captureLogs(events: SentryLogEvents, scope: IScope?, hint: Hint?) {
override fun captureLog(event: SentryLogEvent, scope: IScope?, hint: Hint?) {
TODO("Not yet implemented")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ sentry.enable-backpressure-handling=true
sentry.enable-spotlight=true
sentry.enablePrettySerializationOutput=false
in-app-includes="io.sentry.samples"
sentry.experimental.logs.enabled=true

# Uncomment and set to true to enable aot compatibility
# This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ class SentryAutoConfigurationTest {
"sentry.cron.default-max-runtime=30",
"sentry.cron.default-timezone=America/New_York",
"sentry.cron.default-failure-issue-threshold=40",
"sentry.cron.default-recovery-threshold=50"
"sentry.cron.default-recovery-threshold=50",
"sentry.experimental.logs.enabled=true",
"sentry.experimental.logs.sample-rate=0.4"
).run {
val options = it.getBean(SentryProperties::class.java)
assertThat(options.readTimeoutMillis).isEqualTo(10)
Expand Down Expand Up @@ -232,6 +234,8 @@ class SentryAutoConfigurationTest {
assertThat(options.cron!!.defaultTimezone).isEqualTo("America/New_York")
assertThat(options.cron!!.defaultFailureIssueThreshold).isEqualTo(40L)
assertThat(options.cron!!.defaultRecoveryThreshold).isEqualTo(50L)
assertThat(options.experimental.logs.isEnabled).isEqualTo(true)
assertThat(options.experimental.logs.sampleRate).isEqualTo(0.4)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ class SentryAutoConfigurationTest {
"sentry.cron.default-max-runtime=30",
"sentry.cron.default-timezone=America/New_York",
"sentry.cron.default-failure-issue-threshold=40",
"sentry.cron.default-recovery-threshold=50"
"sentry.cron.default-recovery-threshold=50",
"sentry.experimental.logs.enabled=true",
"sentry.experimental.logs.sample-rate=0.4"
).run {
val options = it.getBean(SentryProperties::class.java)
assertThat(options.readTimeoutMillis).isEqualTo(10)
Expand Down Expand Up @@ -231,6 +233,8 @@ class SentryAutoConfigurationTest {
assertThat(options.cron!!.defaultTimezone).isEqualTo("America/New_York")
assertThat(options.cron!!.defaultFailureIssueThreshold).isEqualTo(40L)
assertThat(options.cron!!.defaultRecoveryThreshold).isEqualTo(50L)
assertThat(options.experimental.logs.isEnabled).isEqualTo(true)
assertThat(options.experimental.logs.sampleRate).isEqualTo(0.4)
}
}

Expand Down
31 changes: 27 additions & 4 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ public final class io/sentry/DataCategory : java/lang/Enum {
public static final field Attachment Lio/sentry/DataCategory;
public static final field Default Lio/sentry/DataCategory;
public static final field Error Lio/sentry/DataCategory;
public static final field LogItem Lio/sentry/DataCategory;
public static final field Monitor Lio/sentry/DataCategory;
public static final field Profile Lio/sentry/DataCategory;
public static final field ProfileChunkUi Lio/sentry/DataCategory;
Expand Down Expand Up @@ -462,6 +463,8 @@ public abstract interface class io/sentry/EventProcessor {

public final class io/sentry/ExperimentalOptions {
public fun <init> (ZLio/sentry/protocol/SdkVersion;)V
public fun getLogs ()Lio/sentry/SentryOptions$Logs;
public fun setLogs (Lio/sentry/SentryOptions$Logs;)V
}

public final class io/sentry/ExternalOptions {
Expand Down Expand Up @@ -489,6 +492,7 @@ public final class io/sentry/ExternalOptions {
public fun getIgnoredTransactions ()Ljava/util/List;
public fun getInAppExcludes ()Ljava/util/List;
public fun getInAppIncludes ()Ljava/util/List;
public fun getLogsSampleRate ()Ljava/lang/Double;
public fun getMaxRequestBodySize ()Lio/sentry/SentryOptions$RequestSize;
public fun getPrintUncaughtStackTrace ()Ljava/lang/Boolean;
public fun getProfilesSampleRate ()Ljava/lang/Double;
Expand All @@ -503,6 +507,7 @@ public final class io/sentry/ExternalOptions {
public fun getTracesSampleRate ()Ljava/lang/Double;
public fun isCaptureOpenTelemetryEvents ()Ljava/lang/Boolean;
public fun isEnableBackpressureHandling ()Ljava/lang/Boolean;
public fun isEnableLogs ()Ljava/lang/Boolean;
public fun isEnablePrettySerializationOutput ()Ljava/lang/Boolean;
public fun isEnableSpotlight ()Ljava/lang/Boolean;
public fun isEnabled ()Ljava/lang/Boolean;
Expand All @@ -517,6 +522,7 @@ public final class io/sentry/ExternalOptions {
public fun setDsn (Ljava/lang/String;)V
public fun setEnableBackpressureHandling (Ljava/lang/Boolean;)V
public fun setEnableDeduplication (Ljava/lang/Boolean;)V
public fun setEnableLogs (Ljava/lang/Boolean;)V
public fun setEnablePrettySerializationOutput (Ljava/lang/Boolean;)V
public fun setEnableSpotlight (Ljava/lang/Boolean;)V
public fun setEnableUncaughtExceptionHandler (Ljava/lang/Boolean;)V
Expand All @@ -528,6 +534,7 @@ public final class io/sentry/ExternalOptions {
public fun setIgnoredCheckIns (Ljava/util/List;)V
public fun setIgnoredErrors (Ljava/util/List;)V
public fun setIgnoredTransactions (Ljava/util/List;)V
public fun setLogsSampleRate (Ljava/lang/Double;)V
public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V
public fun setPrintUncaughtStackTrace (Ljava/lang/Boolean;)V
public fun setProfilesSampleRate (Ljava/lang/Double;)V
Expand Down Expand Up @@ -991,7 +998,7 @@ public abstract interface class io/sentry/ISentryClient {
public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
public fun captureException (Ljava/lang/Throwable;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
public fun captureException (Ljava/lang/Throwable;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
public abstract fun captureLogs (Lio/sentry/SentryLogEvents;Lio/sentry/IScope;Lio/sentry/Hint;)V
public abstract fun captureLog (Lio/sentry/SentryLogEvent;Lio/sentry/IScope;Lio/sentry/Hint;)V
public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId;
public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
public abstract fun captureProfileChunk (Lio/sentry/ProfileChunk;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
Expand Down Expand Up @@ -2714,7 +2721,7 @@ public final class io/sentry/SentryClient : io/sentry/ISentryClient {
public fun captureCheckIn (Lio/sentry/CheckIn;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
public fun captureLogs (Lio/sentry/SentryLogEvents;Lio/sentry/IScope;Lio/sentry/Hint;)V
public fun captureLog (Lio/sentry/SentryLogEvent;Lio/sentry/IScope;Lio/sentry/Hint;)V
public fun captureProfileChunk (Lio/sentry/ProfileChunk;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
public fun captureReplayEvent (Lio/sentry/SentryReplayEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
public fun captureSession (Lio/sentry/Session;Lio/sentry/Hint;)V
Expand Down Expand Up @@ -3013,14 +3020,16 @@ public final class io/sentry/SentryLockReason$JsonKeys {
}

public final class io/sentry/SentryLogEvent : io/sentry/JsonSerializable, io/sentry/JsonUnknown {
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SentryDate;Ljava/lang/String;)V
public fun <init> (Lio/sentry/protocol/SentryId;Ljava/lang/Double;Ljava/lang/String;)V
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SentryDate;Ljava/lang/String;Lio/sentry/SentryLevel;)V
public fun <init> (Lio/sentry/protocol/SentryId;Ljava/lang/Double;Ljava/lang/String;Lio/sentry/SentryLevel;)V
public fun getAttributes ()Ljava/util/Map;
public fun getBody ()Ljava/lang/String;
public fun getLevel ()Lio/sentry/SentryLevel;
public fun getTimestamp ()Ljava/lang/Double;
public fun getUnknown ()Ljava/util/Map;
public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V
public fun setAttributes (Ljava/util/Map;)V
public fun setBody (Ljava/lang/String;)V
public fun setLevel (Lio/sentry/SentryLevel;)V
public fun setTimestamp (Ljava/lang/Double;)V
public fun setUnknown (Ljava/util/Map;)V
Expand Down Expand Up @@ -3401,6 +3410,20 @@ public final class io/sentry/SentryOptions$Cron {
public fun setDefaultTimezone (Ljava/lang/String;)V
}

public final class io/sentry/SentryOptions$Logs {
public fun <init> ()V
public fun getBeforeSend ()Lio/sentry/SentryOptions$Logs$BeforeSendLogCallback;
public fun getSampleRate ()Ljava/lang/Double;
public fun isEnabled ()Z
public fun setBeforeSend (Lio/sentry/SentryOptions$Logs$BeforeSendLogCallback;)V
public fun setEnabled (Z)V
public fun setSampleRate (Ljava/lang/Double;)V
}

public abstract interface class io/sentry/SentryOptions$Logs$BeforeSendLogCallback {
public abstract fun execute (Lio/sentry/SentryLogEvent;Lio/sentry/Hint;)Lio/sentry/SentryLogEvent;
}

public abstract interface class io/sentry/SentryOptions$ProfilesSamplerCallback {
public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double;
}
Expand Down
13 changes: 13 additions & 0 deletions sentry/src/main/java/io/sentry/ExperimentalOptions.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.sentry;

import io.sentry.protocol.SdkVersion;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
Expand All @@ -10,6 +12,17 @@
* <p>Beware that experimental options can change at any time.
*/
public final class ExperimentalOptions {
private @NotNull SentryOptions.Logs logs = new SentryOptions.Logs();

public ExperimentalOptions(final boolean empty, final @Nullable SdkVersion sdkVersion) {}

@ApiStatus.Experimental
public @NotNull SentryOptions.Logs getLogs() {
return logs;
}

@ApiStatus.Experimental
public void setLogs(@NotNull SentryOptions.Logs logs) {
this.logs = logs;
}
}
25 changes: 25 additions & 0 deletions sentry/src/main/java/io/sentry/ExternalOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public final class ExternalOptions {
private @Nullable Boolean enableDeduplication;
private @Nullable Double tracesSampleRate;
private @Nullable Double profilesSampleRate;
private @Nullable Double logsSampleRate;
private @Nullable SentryOptions.RequestSize maxRequestBodySize;
private final @NotNull Map<String, @NotNull String> tags = new ConcurrentHashMap<>();
private @Nullable SentryOptions.Proxy proxy;
Expand All @@ -43,6 +44,7 @@ public final class ExternalOptions {
private @Nullable Boolean enabled;
private @Nullable Boolean enablePrettySerializationOutput;
private @Nullable Boolean enableSpotlight;
private @Nullable Boolean enableLogs;
private @Nullable String spotlightConnectionUrl;

private @Nullable List<String> ignoredCheckIns;
Expand Down Expand Up @@ -150,6 +152,9 @@ public final class ExternalOptions {
options.setCaptureOpenTelemetryEvents(
propertiesProvider.getBooleanProperty("capture-open-telemetry-events"));

options.setEnableLogs(propertiesProvider.getBooleanProperty("logs.enabled"));
options.setLogsSampleRate(propertiesProvider.getDoubleProperty("logs.sample-rate"));

for (final String ignoredExceptionType :
propertiesProvider.getList("ignored-exceptions-for-type")) {
try {
Expand Down Expand Up @@ -518,4 +523,24 @@ public void setCaptureOpenTelemetryEvents(final @Nullable Boolean captureOpenTel
public @Nullable Boolean isCaptureOpenTelemetryEvents() {
return captureOpenTelemetryEvents;
}

@ApiStatus.Experimental
public void setEnableLogs(final @Nullable Boolean enableLogs) {
this.enableLogs = enableLogs;
}

@ApiStatus.Experimental
public @Nullable Boolean isEnableLogs() {
return enableLogs;
}

@ApiStatus.Experimental
public @Nullable Double getLogsSampleRate() {
return logsSampleRate;
}

@ApiStatus.Experimental
public void setLogsSampleRate(final @Nullable Double logsSampleRate) {
this.logsSampleRate = logsSampleRate;
}
}
2 changes: 1 addition & 1 deletion sentry/src/main/java/io/sentry/ISentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ SentryId captureProfileChunk(
SentryId captureCheckIn(@NotNull CheckIn checkIn, @Nullable IScope scope, @Nullable Hint hint);

@ApiStatus.Experimental
void captureLogs(@NotNull SentryLogEvents logEvents, @Nullable IScope scope, @Nullable Hint hint);
void captureLog(@NotNull SentryLogEvent logEvent, @Nullable IScope scope, @Nullable Hint hint);

@ApiStatus.Internal
@Nullable
Expand Down
4 changes: 2 additions & 2 deletions sentry/src/main/java/io/sentry/NoOpSentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ public SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint

@ApiStatus.Experimental
@Override
public void captureLogs(
@NotNull SentryLogEvents logEvents, @Nullable IScope scope, @Nullable Hint hint) {
public void captureLog(
@NotNull SentryLogEvent logEvent, @Nullable IScope scope, @Nullable Hint hint) {
// do nothing
}

Expand Down
65 changes: 51 additions & 14 deletions sentry/src/main/java/io/sentry/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
Expand Down Expand Up @@ -1011,28 +1012,42 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint

@ApiStatus.Experimental
@Override
public void captureLogs(
@NotNull SentryLogEvents logEvents, @Nullable IScope scope, @Nullable Hint hint) {
public void captureLog(
@Nullable SentryLogEvent logEvent, @Nullable IScope scope, @Nullable Hint hint) {
if (hint == null) {
hint = new Hint();
}

try {
@Nullable TraceContext traceContext = null;
if (scope != null) {
final @Nullable ITransaction transaction = scope.getTransaction();
if (transaction != null) {
traceContext = transaction.traceContext();
} else {
final @NotNull PropagationContext propagationContext =
TracingUtils.maybeUpdateBaggage(scope, options);
traceContext = propagationContext.traceContext();
}
@Nullable TraceContext traceContext = null;
if (scope != null) {
final @Nullable ITransaction transaction = scope.getTransaction();
if (transaction != null) {
traceContext = transaction.traceContext();
} else {
final @NotNull PropagationContext propagationContext =
TracingUtils.maybeUpdateBaggage(scope, options);
traceContext = propagationContext.traceContext();
}
}

if (logEvent != null) {
logEvent = executeBeforeSendLog(logEvent, hint);

if (logEvent == null) {
options.getLogger().log(SentryLevel.DEBUG, "Log Event was dropped by beforeSendLog");
options
.getClientReportRecorder()
.recordLostEvent(DiscardReason.BEFORE_SEND, DataCategory.LogItem);
return;
}
}

final @NotNull SentryEnvelope envelope = buildEnvelope(logEvents, traceContext);
try {
final @NotNull SentryEnvelope envelope =
buildEnvelope(new SentryLogEvents(Arrays.asList(logEvent)), traceContext);

hint.clear();
// TODO buffer
sendEnvelope(envelope, hint);
} catch (IOException e) {
options.getLogger().log(SentryLevel.WARNING, e, "Capturing log failed.");
Expand Down Expand Up @@ -1260,6 +1275,28 @@ private void sortBreadcrumbsByDate(
return event;
}

private @Nullable SentryLogEvent executeBeforeSendLog(
@NotNull SentryLogEvent event, final @NotNull Hint hint) {
final SentryOptions.Logs.BeforeSendLogCallback beforeSendLog =
options.getExperimental().getLogs().getBeforeSend();
if (beforeSendLog != null) {
try {
event = beforeSendLog.execute(event, hint);
} catch (Throwable e) {
options
.getLogger()
.log(
SentryLevel.ERROR,
"The BeforeSendLog callback threw an exception. Dropping log event.",
e);

// drop event in case of an error in beforeSendLog due to PII concerns
event = null;
}
}
return event;
}

@Override
public void close() {
close(false);
Expand Down
Loading