Skip to content

Commit 8a19857

Browse files
authored
[Logs 3] Add options for Logs (#4374)
* Add Log feature to Java SDK * Rate limit for log items * Add options for logs
1 parent c608602 commit 8a19857

File tree

17 files changed

+343
-51
lines changed

17 files changed

+343
-51
lines changed

sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import io.sentry.ProfilingTraceData
1616
import io.sentry.Sentry
1717
import io.sentry.SentryEnvelope
1818
import io.sentry.SentryEvent
19-
import io.sentry.SentryLogEvents
19+
import io.sentry.SentryLogEvent
2020
import io.sentry.SentryOptions
2121
import io.sentry.SentryReplayEvent
2222
import io.sentry.Session
@@ -191,7 +191,7 @@ class SessionTrackingIntegrationTest {
191191
TODO("Not yet implemented")
192192
}
193193

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

sentry-samples/sentry-samples-spring-boot-jakarta/src/main/resources/application.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ sentry.enable-backpressure-handling=true
1616
sentry.enable-spotlight=true
1717
sentry.enablePrettySerializationOutput=false
1818
in-app-includes="io.sentry.samples"
19+
sentry.experimental.logs.enabled=true
1920

2021
# Uncomment and set to true to enable aot compatibility
2122
# This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan)

sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ class SentryAutoConfigurationTest {
187187
"sentry.cron.default-max-runtime=30",
188188
"sentry.cron.default-timezone=America/New_York",
189189
"sentry.cron.default-failure-issue-threshold=40",
190-
"sentry.cron.default-recovery-threshold=50"
190+
"sentry.cron.default-recovery-threshold=50",
191+
"sentry.experimental.logs.enabled=true",
192+
"sentry.experimental.logs.sample-rate=0.4"
191193
).run {
192194
val options = it.getBean(SentryProperties::class.java)
193195
assertThat(options.readTimeoutMillis).isEqualTo(10)
@@ -232,6 +234,8 @@ class SentryAutoConfigurationTest {
232234
assertThat(options.cron!!.defaultTimezone).isEqualTo("America/New_York")
233235
assertThat(options.cron!!.defaultFailureIssueThreshold).isEqualTo(40L)
234236
assertThat(options.cron!!.defaultRecoveryThreshold).isEqualTo(50L)
237+
assertThat(options.experimental.logs.isEnabled).isEqualTo(true)
238+
assertThat(options.experimental.logs.sampleRate).isEqualTo(0.4)
235239
}
236240
}
237241

sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ class SentryAutoConfigurationTest {
186186
"sentry.cron.default-max-runtime=30",
187187
"sentry.cron.default-timezone=America/New_York",
188188
"sentry.cron.default-failure-issue-threshold=40",
189-
"sentry.cron.default-recovery-threshold=50"
189+
"sentry.cron.default-recovery-threshold=50",
190+
"sentry.experimental.logs.enabled=true",
191+
"sentry.experimental.logs.sample-rate=0.4"
190192
).run {
191193
val options = it.getBean(SentryProperties::class.java)
192194
assertThat(options.readTimeoutMillis).isEqualTo(10)
@@ -231,6 +233,8 @@ class SentryAutoConfigurationTest {
231233
assertThat(options.cron!!.defaultTimezone).isEqualTo("America/New_York")
232234
assertThat(options.cron!!.defaultFailureIssueThreshold).isEqualTo(40L)
233235
assertThat(options.cron!!.defaultRecoveryThreshold).isEqualTo(50L)
236+
assertThat(options.experimental.logs.isEnabled).isEqualTo(true)
237+
assertThat(options.experimental.logs.sampleRate).isEqualTo(0.4)
234238
}
235239
}
236240

sentry/api/sentry.api

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,8 @@ public abstract interface class io/sentry/EventProcessor {
464464

465465
public final class io/sentry/ExperimentalOptions {
466466
public fun <init> (ZLio/sentry/protocol/SdkVersion;)V
467+
public fun getLogs ()Lio/sentry/SentryOptions$Logs;
468+
public fun setLogs (Lio/sentry/SentryOptions$Logs;)V
467469
}
468470

469471
public final class io/sentry/ExternalOptions {
@@ -491,6 +493,7 @@ public final class io/sentry/ExternalOptions {
491493
public fun getIgnoredTransactions ()Ljava/util/List;
492494
public fun getInAppExcludes ()Ljava/util/List;
493495
public fun getInAppIncludes ()Ljava/util/List;
496+
public fun getLogsSampleRate ()Ljava/lang/Double;
494497
public fun getMaxRequestBodySize ()Lio/sentry/SentryOptions$RequestSize;
495498
public fun getPrintUncaughtStackTrace ()Ljava/lang/Boolean;
496499
public fun getProfilesSampleRate ()Ljava/lang/Double;
@@ -505,6 +508,7 @@ public final class io/sentry/ExternalOptions {
505508
public fun getTracesSampleRate ()Ljava/lang/Double;
506509
public fun isCaptureOpenTelemetryEvents ()Ljava/lang/Boolean;
507510
public fun isEnableBackpressureHandling ()Ljava/lang/Boolean;
511+
public fun isEnableLogs ()Ljava/lang/Boolean;
508512
public fun isEnablePrettySerializationOutput ()Ljava/lang/Boolean;
509513
public fun isEnableSpotlight ()Ljava/lang/Boolean;
510514
public fun isEnabled ()Ljava/lang/Boolean;
@@ -519,6 +523,7 @@ public final class io/sentry/ExternalOptions {
519523
public fun setDsn (Ljava/lang/String;)V
520524
public fun setEnableBackpressureHandling (Ljava/lang/Boolean;)V
521525
public fun setEnableDeduplication (Ljava/lang/Boolean;)V
526+
public fun setEnableLogs (Ljava/lang/Boolean;)V
522527
public fun setEnablePrettySerializationOutput (Ljava/lang/Boolean;)V
523528
public fun setEnableSpotlight (Ljava/lang/Boolean;)V
524529
public fun setEnableUncaughtExceptionHandler (Ljava/lang/Boolean;)V
@@ -530,6 +535,7 @@ public final class io/sentry/ExternalOptions {
530535
public fun setIgnoredCheckIns (Ljava/util/List;)V
531536
public fun setIgnoredErrors (Ljava/util/List;)V
532537
public fun setIgnoredTransactions (Ljava/util/List;)V
538+
public fun setLogsSampleRate (Ljava/lang/Double;)V
533539
public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V
534540
public fun setPrintUncaughtStackTrace (Ljava/lang/Boolean;)V
535541
public fun setProfilesSampleRate (Ljava/lang/Double;)V
@@ -1001,7 +1007,7 @@ public abstract interface class io/sentry/ISentryClient {
10011007
public fun captureException (Ljava/lang/Throwable;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
10021008
public fun captureException (Ljava/lang/Throwable;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
10031009
public abstract fun captureFeedback (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
1004-
public abstract fun captureLogs (Lio/sentry/SentryLogEvents;Lio/sentry/IScope;Lio/sentry/Hint;)V
1010+
public abstract fun captureLog (Lio/sentry/SentryLogEvent;Lio/sentry/IScope;Lio/sentry/Hint;)V
10051011
public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId;
10061012
public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
10071013
public abstract fun captureProfileChunk (Lio/sentry/ProfileChunk;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
@@ -2734,7 +2740,7 @@ public final class io/sentry/SentryClient : io/sentry/ISentryClient {
27342740
public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
27352741
public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
27362742
public fun captureFeedback (Lio/sentry/protocol/Feedback;Lio/sentry/Hint;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
2737-
public fun captureLogs (Lio/sentry/SentryLogEvents;Lio/sentry/IScope;Lio/sentry/Hint;)V
2743+
public fun captureLog (Lio/sentry/SentryLogEvent;Lio/sentry/IScope;Lio/sentry/Hint;)V
27382744
public fun captureProfileChunk (Lio/sentry/ProfileChunk;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
27392745
public fun captureReplayEvent (Lio/sentry/SentryReplayEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
27402746
public fun captureSession (Lio/sentry/Session;Lio/sentry/Hint;)V
@@ -3033,14 +3039,16 @@ public final class io/sentry/SentryLockReason$JsonKeys {
30333039
}
30343040

30353041
public final class io/sentry/SentryLogEvent : io/sentry/JsonSerializable, io/sentry/JsonUnknown {
3036-
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SentryDate;Ljava/lang/String;)V
3037-
public fun <init> (Lio/sentry/protocol/SentryId;Ljava/lang/Double;Ljava/lang/String;)V
3042+
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SentryDate;Ljava/lang/String;Lio/sentry/SentryLevel;)V
3043+
public fun <init> (Lio/sentry/protocol/SentryId;Ljava/lang/Double;Ljava/lang/String;Lio/sentry/SentryLevel;)V
30383044
public fun getAttributes ()Ljava/util/Map;
3045+
public fun getBody ()Ljava/lang/String;
30393046
public fun getLevel ()Lio/sentry/SentryLevel;
30403047
public fun getTimestamp ()Ljava/lang/Double;
30413048
public fun getUnknown ()Ljava/util/Map;
30423049
public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V
30433050
public fun setAttributes (Ljava/util/Map;)V
3051+
public fun setBody (Ljava/lang/String;)V
30443052
public fun setLevel (Lio/sentry/SentryLevel;)V
30453053
public fun setTimestamp (Ljava/lang/Double;)V
30463054
public fun setUnknown (Ljava/util/Map;)V
@@ -3423,6 +3431,20 @@ public final class io/sentry/SentryOptions$Cron {
34233431
public fun setDefaultTimezone (Ljava/lang/String;)V
34243432
}
34253433

3434+
public final class io/sentry/SentryOptions$Logs {
3435+
public fun <init> ()V
3436+
public fun getBeforeSend ()Lio/sentry/SentryOptions$Logs$BeforeSendLogCallback;
3437+
public fun getSampleRate ()Ljava/lang/Double;
3438+
public fun isEnabled ()Z
3439+
public fun setBeforeSend (Lio/sentry/SentryOptions$Logs$BeforeSendLogCallback;)V
3440+
public fun setEnabled (Z)V
3441+
public fun setSampleRate (Ljava/lang/Double;)V
3442+
}
3443+
3444+
public abstract interface class io/sentry/SentryOptions$Logs$BeforeSendLogCallback {
3445+
public abstract fun execute (Lio/sentry/SentryLogEvent;Lio/sentry/Hint;)Lio/sentry/SentryLogEvent;
3446+
}
3447+
34263448
public abstract interface class io/sentry/SentryOptions$ProfilesSamplerCallback {
34273449
public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double;
34283450
}

sentry/src/main/java/io/sentry/ExperimentalOptions.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.sentry;
22

33
import io.sentry.protocol.SdkVersion;
4+
import org.jetbrains.annotations.ApiStatus;
5+
import org.jetbrains.annotations.NotNull;
46
import org.jetbrains.annotations.Nullable;
57

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

1417
public ExperimentalOptions(final boolean empty, final @Nullable SdkVersion sdkVersion) {}
18+
19+
@ApiStatus.Experimental
20+
public @NotNull SentryOptions.Logs getLogs() {
21+
return logs;
22+
}
23+
24+
@ApiStatus.Experimental
25+
public void setLogs(@NotNull SentryOptions.Logs logs) {
26+
this.logs = logs;
27+
}
1528
}

sentry/src/main/java/io/sentry/ExternalOptions.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public final class ExternalOptions {
2525
private @Nullable Boolean enableDeduplication;
2626
private @Nullable Double tracesSampleRate;
2727
private @Nullable Double profilesSampleRate;
28+
private @Nullable Double logsSampleRate;
2829
private @Nullable SentryOptions.RequestSize maxRequestBodySize;
2930
private final @NotNull Map<String, @NotNull String> tags = new ConcurrentHashMap<>();
3031
private @Nullable SentryOptions.Proxy proxy;
@@ -43,6 +44,7 @@ public final class ExternalOptions {
4344
private @Nullable Boolean enabled;
4445
private @Nullable Boolean enablePrettySerializationOutput;
4546
private @Nullable Boolean enableSpotlight;
47+
private @Nullable Boolean enableLogs;
4648
private @Nullable String spotlightConnectionUrl;
4749

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

155+
options.setEnableLogs(propertiesProvider.getBooleanProperty("logs.enabled"));
156+
options.setLogsSampleRate(propertiesProvider.getDoubleProperty("logs.sample-rate"));
157+
153158
for (final String ignoredExceptionType :
154159
propertiesProvider.getList("ignored-exceptions-for-type")) {
155160
try {
@@ -518,4 +523,24 @@ public void setCaptureOpenTelemetryEvents(final @Nullable Boolean captureOpenTel
518523
public @Nullable Boolean isCaptureOpenTelemetryEvents() {
519524
return captureOpenTelemetryEvents;
520525
}
526+
527+
@ApiStatus.Experimental
528+
public void setEnableLogs(final @Nullable Boolean enableLogs) {
529+
this.enableLogs = enableLogs;
530+
}
531+
532+
@ApiStatus.Experimental
533+
public @Nullable Boolean isEnableLogs() {
534+
return enableLogs;
535+
}
536+
537+
@ApiStatus.Experimental
538+
public @Nullable Double getLogsSampleRate() {
539+
return logsSampleRate;
540+
}
541+
542+
@ApiStatus.Experimental
543+
public void setLogsSampleRate(final @Nullable Double logsSampleRate) {
544+
this.logsSampleRate = logsSampleRate;
545+
}
521546
}

sentry/src/main/java/io/sentry/ISentryClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ SentryId captureProfileChunk(
305305
SentryId captureCheckIn(@NotNull CheckIn checkIn, @Nullable IScope scope, @Nullable Hint hint);
306306

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

310310
@ApiStatus.Internal
311311
@Nullable

sentry/src/main/java/io/sentry/NoOpSentryClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ public SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint
8686

8787
@ApiStatus.Experimental
8888
@Override
89-
public void captureLogs(
90-
@NotNull SentryLogEvents logEvents, @Nullable IScope scope, @Nullable Hint hint) {
89+
public void captureLog(
90+
@NotNull SentryLogEvent logEvent, @Nullable IScope scope, @Nullable Hint hint) {
9191
// do nothing
9292
}
9393

sentry/src/main/java/io/sentry/SentryClient.java

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.io.Closeable;
1818
import java.io.IOException;
1919
import java.util.ArrayList;
20+
import java.util.Arrays;
2021
import java.util.Collection;
2122
import java.util.Collections;
2223
import java.util.Comparator;
@@ -1124,28 +1125,42 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
11241125

11251126
@ApiStatus.Experimental
11261127
@Override
1127-
public void captureLogs(
1128-
@NotNull SentryLogEvents logEvents, @Nullable IScope scope, @Nullable Hint hint) {
1128+
public void captureLog(
1129+
@Nullable SentryLogEvent logEvent, @Nullable IScope scope, @Nullable Hint hint) {
11291130
if (hint == null) {
11301131
hint = new Hint();
11311132
}
11321133

1133-
try {
1134-
@Nullable TraceContext traceContext = null;
1135-
if (scope != null) {
1136-
final @Nullable ITransaction transaction = scope.getTransaction();
1137-
if (transaction != null) {
1138-
traceContext = transaction.traceContext();
1139-
} else {
1140-
final @NotNull PropagationContext propagationContext =
1141-
TracingUtils.maybeUpdateBaggage(scope, options);
1142-
traceContext = propagationContext.traceContext();
1143-
}
1134+
@Nullable TraceContext traceContext = null;
1135+
if (scope != null) {
1136+
final @Nullable ITransaction transaction = scope.getTransaction();
1137+
if (transaction != null) {
1138+
traceContext = transaction.traceContext();
1139+
} else {
1140+
final @NotNull PropagationContext propagationContext =
1141+
TracingUtils.maybeUpdateBaggage(scope, options);
1142+
traceContext = propagationContext.traceContext();
1143+
}
1144+
}
1145+
1146+
if (logEvent != null) {
1147+
logEvent = executeBeforeSendLog(logEvent, hint);
1148+
1149+
if (logEvent == null) {
1150+
options.getLogger().log(SentryLevel.DEBUG, "Log Event was dropped by beforeSendLog");
1151+
options
1152+
.getClientReportRecorder()
1153+
.recordLostEvent(DiscardReason.BEFORE_SEND, DataCategory.LogItem);
1154+
return;
11441155
}
1156+
}
11451157

1146-
final @NotNull SentryEnvelope envelope = buildEnvelope(logEvents, traceContext);
1158+
try {
1159+
final @NotNull SentryEnvelope envelope =
1160+
buildEnvelope(new SentryLogEvents(Arrays.asList(logEvent)), traceContext);
11471161

11481162
hint.clear();
1163+
// TODO buffer
11491164
sendEnvelope(envelope, hint);
11501165
} catch (IOException e) {
11511166
options.getLogger().log(SentryLevel.WARNING, e, "Capturing log failed.");
@@ -1428,6 +1443,28 @@ private void sortBreadcrumbsByDate(
14281443
return event;
14291444
}
14301445

1446+
private @Nullable SentryLogEvent executeBeforeSendLog(
1447+
@NotNull SentryLogEvent event, final @NotNull Hint hint) {
1448+
final SentryOptions.Logs.BeforeSendLogCallback beforeSendLog =
1449+
options.getExperimental().getLogs().getBeforeSend();
1450+
if (beforeSendLog != null) {
1451+
try {
1452+
event = beforeSendLog.execute(event, hint);
1453+
} catch (Throwable e) {
1454+
options
1455+
.getLogger()
1456+
.log(
1457+
SentryLevel.ERROR,
1458+
"The BeforeSendLog callback threw an exception. Dropping log event.",
1459+
e);
1460+
1461+
// drop event in case of an error in beforeSendLog due to PII concerns
1462+
event = null;
1463+
}
1464+
}
1465+
return event;
1466+
}
1467+
14311468
@Override
14321469
public void close() {
14331470
close(false);

0 commit comments

Comments
 (0)