Skip to content

Commit 0c75319

Browse files
committed
Add options for logs
1 parent 27a24ec commit 0c75319

17 files changed

Lines changed: 351 additions & 53 deletions

File tree

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
@@ -186,7 +186,7 @@ class SessionTrackingIntegrationTest {
186186
TODO("Not yet implemented")
187187
}
188188

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

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: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ public final class io/sentry/DataCategory : java/lang/Enum {
352352
public static final field Attachment Lio/sentry/DataCategory;
353353
public static final field Default Lio/sentry/DataCategory;
354354
public static final field Error Lio/sentry/DataCategory;
355+
public static final field LogItem Lio/sentry/DataCategory;
355356
public static final field Monitor Lio/sentry/DataCategory;
356357
public static final field Profile Lio/sentry/DataCategory;
357358
public static final field ProfileChunkUi Lio/sentry/DataCategory;
@@ -462,6 +463,8 @@ public abstract interface class io/sentry/EventProcessor {
462463

463464
public final class io/sentry/ExperimentalOptions {
464465
public fun <init> (ZLio/sentry/protocol/SdkVersion;)V
466+
public fun getLogs ()Lio/sentry/SentryOptions$Logs;
467+
public fun setLogs (Lio/sentry/SentryOptions$Logs;)V
465468
}
466469

467470
public final class io/sentry/ExternalOptions {
@@ -489,6 +492,7 @@ public final class io/sentry/ExternalOptions {
489492
public fun getIgnoredTransactions ()Ljava/util/List;
490493
public fun getInAppExcludes ()Ljava/util/List;
491494
public fun getInAppIncludes ()Ljava/util/List;
495+
public fun getLogsSampleRate ()Ljava/lang/Double;
492496
public fun getMaxRequestBodySize ()Lio/sentry/SentryOptions$RequestSize;
493497
public fun getPrintUncaughtStackTrace ()Ljava/lang/Boolean;
494498
public fun getProfilesSampleRate ()Ljava/lang/Double;
@@ -503,6 +507,7 @@ public final class io/sentry/ExternalOptions {
503507
public fun getTracesSampleRate ()Ljava/lang/Double;
504508
public fun isCaptureOpenTelemetryEvents ()Ljava/lang/Boolean;
505509
public fun isEnableBackpressureHandling ()Ljava/lang/Boolean;
510+
public fun isEnableLogs ()Ljava/lang/Boolean;
506511
public fun isEnablePrettySerializationOutput ()Ljava/lang/Boolean;
507512
public fun isEnableSpotlight ()Ljava/lang/Boolean;
508513
public fun isEnabled ()Ljava/lang/Boolean;
@@ -517,6 +522,7 @@ public final class io/sentry/ExternalOptions {
517522
public fun setDsn (Ljava/lang/String;)V
518523
public fun setEnableBackpressureHandling (Ljava/lang/Boolean;)V
519524
public fun setEnableDeduplication (Ljava/lang/Boolean;)V
525+
public fun setEnableLogs (Ljava/lang/Boolean;)V
520526
public fun setEnablePrettySerializationOutput (Ljava/lang/Boolean;)V
521527
public fun setEnableSpotlight (Ljava/lang/Boolean;)V
522528
public fun setEnableUncaughtExceptionHandler (Ljava/lang/Boolean;)V
@@ -528,6 +534,7 @@ public final class io/sentry/ExternalOptions {
528534
public fun setIgnoredCheckIns (Ljava/util/List;)V
529535
public fun setIgnoredErrors (Ljava/util/List;)V
530536
public fun setIgnoredTransactions (Ljava/util/List;)V
537+
public fun setLogsSampleRate (Ljava/lang/Double;)V
531538
public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V
532539
public fun setPrintUncaughtStackTrace (Ljava/lang/Boolean;)V
533540
public fun setProfilesSampleRate (Ljava/lang/Double;)V
@@ -991,7 +998,7 @@ public abstract interface class io/sentry/ISentryClient {
991998
public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
992999
public fun captureException (Ljava/lang/Throwable;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
9931000
public fun captureException (Ljava/lang/Throwable;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
994-
public abstract fun captureLogs (Lio/sentry/SentryLogEvents;Lio/sentry/IScope;Lio/sentry/Hint;)V
1001+
public abstract fun captureLog (Lio/sentry/SentryLogEvent;Lio/sentry/IScope;Lio/sentry/Hint;)V
9951002
public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId;
9961003
public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
9971004
public abstract fun captureProfileChunk (Lio/sentry/ProfileChunk;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
@@ -2714,7 +2721,7 @@ public final class io/sentry/SentryClient : io/sentry/ISentryClient {
27142721
public fun captureCheckIn (Lio/sentry/CheckIn;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
27152722
public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
27162723
public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
2717-
public fun captureLogs (Lio/sentry/SentryLogEvents;Lio/sentry/IScope;Lio/sentry/Hint;)V
2724+
public fun captureLog (Lio/sentry/SentryLogEvent;Lio/sentry/IScope;Lio/sentry/Hint;)V
27182725
public fun captureProfileChunk (Lio/sentry/ProfileChunk;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId;
27192726
public fun captureReplayEvent (Lio/sentry/SentryReplayEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId;
27202727
public fun captureSession (Lio/sentry/Session;Lio/sentry/Hint;)V
@@ -3013,14 +3020,16 @@ public final class io/sentry/SentryLockReason$JsonKeys {
30133020
}
30143021

30153022
public final class io/sentry/SentryLogEvent : io/sentry/JsonSerializable, io/sentry/JsonUnknown {
3016-
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SentryDate;Ljava/lang/String;)V
3017-
public fun <init> (Lio/sentry/protocol/SentryId;Ljava/lang/Double;Ljava/lang/String;)V
3023+
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SentryDate;Ljava/lang/String;Lio/sentry/SentryLevel;)V
3024+
public fun <init> (Lio/sentry/protocol/SentryId;Ljava/lang/Double;Ljava/lang/String;Lio/sentry/SentryLevel;)V
30183025
public fun getAttributes ()Ljava/util/Map;
3026+
public fun getBody ()Ljava/lang/String;
30193027
public fun getLevel ()Lio/sentry/SentryLevel;
30203028
public fun getTimestamp ()Ljava/lang/Double;
30213029
public fun getUnknown ()Ljava/util/Map;
30223030
public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V
30233031
public fun setAttributes (Ljava/util/Map;)V
3032+
public fun setBody (Ljava/lang/String;)V
30243033
public fun setLevel (Lio/sentry/SentryLevel;)V
30253034
public fun setTimestamp (Ljava/lang/Double;)V
30263035
public fun setUnknown (Ljava/util/Map;)V
@@ -3401,6 +3410,20 @@ public final class io/sentry/SentryOptions$Cron {
34013410
public fun setDefaultTimezone (Ljava/lang/String;)V
34023411
}
34033412

3413+
public final class io/sentry/SentryOptions$Logs {
3414+
public fun <init> ()V
3415+
public fun getBeforeSend ()Lio/sentry/SentryOptions$Logs$BeforeSendLogCallback;
3416+
public fun getSampleRate ()Ljava/lang/Double;
3417+
public fun isEnabled ()Z
3418+
public fun setBeforeSend (Lio/sentry/SentryOptions$Logs$BeforeSendLogCallback;)V
3419+
public fun setEnabled (Z)V
3420+
public fun setSampleRate (Ljava/lang/Double;)V
3421+
}
3422+
3423+
public abstract interface class io/sentry/SentryOptions$Logs$BeforeSendLogCallback {
3424+
public abstract fun execute (Lio/sentry/SentryLogEvent;Lio/sentry/Hint;)Lio/sentry/SentryLogEvent;
3425+
}
3426+
34043427
public abstract interface class io/sentry/SentryOptions$ProfilesSamplerCallback {
34053428
public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double;
34063429
}

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
@@ -293,7 +293,7 @@ SentryId captureProfileChunk(
293293
SentryId captureCheckIn(@NotNull CheckIn checkIn, @Nullable IScope scope, @Nullable Hint hint);
294294

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

298298
@ApiStatus.Internal
299299
@Nullable

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

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

8080
@ApiStatus.Experimental
8181
@Override
82-
public void captureLogs(
83-
@NotNull SentryLogEvents logEvents, @Nullable IScope scope, @Nullable Hint hint) {
82+
public void captureLog(
83+
@NotNull SentryLogEvent logEvent, @Nullable IScope scope, @Nullable Hint hint) {
8484
// do nothing
8585
}
8686

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

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.io.Closeable;
1717
import java.io.IOException;
1818
import java.util.ArrayList;
19+
import java.util.Arrays;
1920
import java.util.Collection;
2021
import java.util.Collections;
2122
import java.util.Comparator;
@@ -1011,28 +1012,42 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
10111012

10121013
@ApiStatus.Experimental
10131014
@Override
1014-
public void captureLogs(
1015-
@NotNull SentryLogEvents logEvents, @Nullable IScope scope, @Nullable Hint hint) {
1015+
public void captureLog(
1016+
@Nullable SentryLogEvent logEvent, @Nullable IScope scope, @Nullable Hint hint) {
10161017
if (hint == null) {
10171018
hint = new Hint();
10181019
}
10191020

1020-
try {
1021-
@Nullable TraceContext traceContext = null;
1022-
if (scope != null) {
1023-
final @Nullable ITransaction transaction = scope.getTransaction();
1024-
if (transaction != null) {
1025-
traceContext = transaction.traceContext();
1026-
} else {
1027-
final @NotNull PropagationContext propagationContext =
1028-
TracingUtils.maybeUpdateBaggage(scope, options);
1029-
traceContext = propagationContext.traceContext();
1030-
}
1021+
@Nullable TraceContext traceContext = null;
1022+
if (scope != null) {
1023+
final @Nullable ITransaction transaction = scope.getTransaction();
1024+
if (transaction != null) {
1025+
traceContext = transaction.traceContext();
1026+
} else {
1027+
final @NotNull PropagationContext propagationContext =
1028+
TracingUtils.maybeUpdateBaggage(scope, options);
1029+
traceContext = propagationContext.traceContext();
1030+
}
1031+
}
1032+
1033+
if (logEvent != null) {
1034+
logEvent = executeBeforeSendLog(logEvent, hint);
1035+
1036+
if (logEvent == null) {
1037+
options.getLogger().log(SentryLevel.DEBUG, "Log Event was dropped by beforeSendLog");
1038+
options
1039+
.getClientReportRecorder()
1040+
.recordLostEvent(DiscardReason.BEFORE_SEND, DataCategory.LogItem);
1041+
return;
10311042
}
1043+
}
10321044

1033-
final @NotNull SentryEnvelope envelope = buildEnvelope(logEvents, traceContext);
1045+
try {
1046+
final @NotNull SentryEnvelope envelope =
1047+
buildEnvelope(new SentryLogEvents(Arrays.asList(logEvent)), traceContext);
10341048

10351049
hint.clear();
1050+
// TODO buffer
10361051
sendEnvelope(envelope, hint);
10371052
} catch (IOException e) {
10381053
options.getLogger().log(SentryLevel.WARNING, e, "Capturing log failed.");
@@ -1260,6 +1275,28 @@ private void sortBreadcrumbsByDate(
12601275
return event;
12611276
}
12621277

1278+
private @Nullable SentryLogEvent executeBeforeSendLog(
1279+
@NotNull SentryLogEvent event, final @NotNull Hint hint) {
1280+
final SentryOptions.Logs.BeforeSendLogCallback beforeSendLog =
1281+
options.getExperimental().getLogs().getBeforeSend();
1282+
if (beforeSendLog != null) {
1283+
try {
1284+
event = beforeSendLog.execute(event, hint);
1285+
} catch (Throwable e) {
1286+
options
1287+
.getLogger()
1288+
.log(
1289+
SentryLevel.ERROR,
1290+
"The BeforeSendLog callback threw an exception. Dropping log event.",
1291+
e);
1292+
1293+
// drop event in case of an error in beforeSendLog due to PII concerns
1294+
event = null;
1295+
}
1296+
}
1297+
return event;
1298+
}
1299+
12631300
@Override
12641301
public void close() {
12651302
close(false);

0 commit comments

Comments
 (0)