Skip to content

Commit e0cc1a5

Browse files
adinauerlcian
andauthored
Extend Logs API to allow passing in attributes (#4402)
* Extend Logs API to allow passing in attributes * update (#4409) * Improve Log Attributes API (#4416) * Improve logger attributes API * Review feedback * changelog --------- Co-authored-by: Lorenzo Cian <lorenzo.cian@sentry.io>
1 parent 0285c02 commit e0cc1a5

File tree

13 files changed

+489
-12
lines changed

13 files changed

+489
-12
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@
77
- Add debug mode for Session Replay masking ([#4357](https://github.com/getsentry/sentry-java/pull/4357))
88
- Use `Sentry.replay().enableDebugMaskingOverlay()` to overlay the screen with the Session Replay masks.
99
- The masks will be invalidated at most once per `frameRate` (default 1 fps).
10+
- Extend Logs API to allow passing in `attributes` ([#4402](https://github.com/getsentry/sentry-java/pull/4402))
11+
- `Sentry.logger.log` now takes a `SentryLogParameters`
12+
- Use `SentryLogParameters.create(SentryAttributes.of(...))` to pass attributes
13+
- Attribute values may be of type `string`, `boolean`, `integer` or `double`.
14+
- Other types will be converted to `string`. Currently we simply call `toString()` but we might offer more in the future.
15+
- You may manually flatten complex types into multiple separate attributes of simple types.
16+
- e.g. intead of `SentryAttribute.named("point", Point(10, 20))` you may store it as `SentryAttribute.integerAttribute("point.x", point.x)` and `SentryAttribute.integerAttribute("point.y", point.y)`
17+
- `SentryAttribute.named()` will automatically infer the type or fall back to `string`.
18+
- `SentryAttribute.booleanAttribute()` takes a `Boolean` value
19+
- `SentryAttribute.integerAttribute()` takes a `Integer` value
20+
- `SentryAttribute.doubleAttribute()` takes a `Double` value
21+
- `SentryAttribute.stringAttribute()` takes a `String` value
22+
- We opted for handling parameters via `SentryLogParameters` to avoid creating tons of overloads that are ambiguous.
1023

1124
## 8.12.0
1225

sentry/api/sentry.api

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2668,6 +2668,34 @@ public final class io/sentry/SentryAppStartProfilingOptions$JsonKeys {
26682668
public fun <init> ()V
26692669
}
26702670

2671+
public final class io/sentry/SentryAttribute {
2672+
public static fun booleanAttribute (Ljava/lang/String;Ljava/lang/Boolean;)Lio/sentry/SentryAttribute;
2673+
public static fun doubleAttribute (Ljava/lang/String;Ljava/lang/Double;)Lio/sentry/SentryAttribute;
2674+
public fun getName ()Ljava/lang/String;
2675+
public fun getType ()Lio/sentry/SentryAttributeType;
2676+
public fun getValue ()Ljava/lang/Object;
2677+
public static fun integerAttribute (Ljava/lang/String;Ljava/lang/Integer;)Lio/sentry/SentryAttribute;
2678+
public static fun named (Ljava/lang/String;Ljava/lang/Object;)Lio/sentry/SentryAttribute;
2679+
public static fun stringAttribute (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/SentryAttribute;
2680+
}
2681+
2682+
public final class io/sentry/SentryAttributeType : java/lang/Enum {
2683+
public static final field BOOLEAN Lio/sentry/SentryAttributeType;
2684+
public static final field DOUBLE Lio/sentry/SentryAttributeType;
2685+
public static final field INTEGER Lio/sentry/SentryAttributeType;
2686+
public static final field STRING Lio/sentry/SentryAttributeType;
2687+
public fun apiName ()Ljava/lang/String;
2688+
public static fun valueOf (Ljava/lang/String;)Lio/sentry/SentryAttributeType;
2689+
public static fun values ()[Lio/sentry/SentryAttributeType;
2690+
}
2691+
2692+
public final class io/sentry/SentryAttributes {
2693+
public fun add (Lio/sentry/SentryAttribute;)V
2694+
public static fun fromMap (Ljava/util/Map;)Lio/sentry/SentryAttributes;
2695+
public fun getAttributes ()Ljava/util/Map;
2696+
public static fun of ([Lio/sentry/SentryAttribute;)Lio/sentry/SentryAttributes;
2697+
}
2698+
26712699
public final class io/sentry/SentryAutoDateProvider : io/sentry/SentryDateProvider {
26722700
public fun <init> ()V
26732701
public fun now ()Lio/sentry/SentryDate;
@@ -3087,6 +3115,7 @@ public final class io/sentry/SentryLogEvent$JsonKeys {
30873115
}
30883116

30893117
public final class io/sentry/SentryLogEventAttributeValue : io/sentry/JsonSerializable, io/sentry/JsonUnknown {
3118+
public fun <init> (Lio/sentry/SentryAttributeType;Ljava/lang/Object;)V
30903119
public fun <init> (Ljava/lang/String;Ljava/lang/Object;)V
30913120
public fun getType ()Ljava/lang/String;
30923121
public fun getUnknown ()Ljava/util/Map;
@@ -4721,6 +4750,7 @@ public abstract interface class io/sentry/logger/ILoggerApi {
47214750
public abstract fun fatal (Ljava/lang/String;[Ljava/lang/Object;)V
47224751
public abstract fun info (Ljava/lang/String;[Ljava/lang/Object;)V
47234752
public abstract fun log (Lio/sentry/SentryLogLevel;Lio/sentry/SentryDate;Ljava/lang/String;[Ljava/lang/Object;)V
4753+
public abstract fun log (Lio/sentry/SentryLogLevel;Lio/sentry/logger/SentryLogParameters;Ljava/lang/String;[Ljava/lang/Object;)V
47244754
public abstract fun log (Lio/sentry/SentryLogLevel;Ljava/lang/String;[Ljava/lang/Object;)V
47254755
public abstract fun trace (Ljava/lang/String;[Ljava/lang/Object;)V
47264756
public abstract fun warn (Ljava/lang/String;[Ljava/lang/Object;)V
@@ -4738,6 +4768,7 @@ public final class io/sentry/logger/LoggerApi : io/sentry/logger/ILoggerApi {
47384768
public fun fatal (Ljava/lang/String;[Ljava/lang/Object;)V
47394769
public fun info (Ljava/lang/String;[Ljava/lang/Object;)V
47404770
public fun log (Lio/sentry/SentryLogLevel;Lio/sentry/SentryDate;Ljava/lang/String;[Ljava/lang/Object;)V
4771+
public fun log (Lio/sentry/SentryLogLevel;Lio/sentry/logger/SentryLogParameters;Ljava/lang/String;[Ljava/lang/Object;)V
47414772
public fun log (Lio/sentry/SentryLogLevel;Ljava/lang/String;[Ljava/lang/Object;)V
47424773
public fun trace (Ljava/lang/String;[Ljava/lang/Object;)V
47434774
public fun warn (Ljava/lang/String;[Ljava/lang/Object;)V
@@ -4758,6 +4789,7 @@ public final class io/sentry/logger/NoOpLoggerApi : io/sentry/logger/ILoggerApi
47584789
public static fun getInstance ()Lio/sentry/logger/NoOpLoggerApi;
47594790
public fun info (Ljava/lang/String;[Ljava/lang/Object;)V
47604791
public fun log (Lio/sentry/SentryLogLevel;Lio/sentry/SentryDate;Ljava/lang/String;[Ljava/lang/Object;)V
4792+
public fun log (Lio/sentry/SentryLogLevel;Lio/sentry/logger/SentryLogParameters;Ljava/lang/String;[Ljava/lang/Object;)V
47614793
public fun log (Lio/sentry/SentryLogLevel;Ljava/lang/String;[Ljava/lang/Object;)V
47624794
public fun trace (Ljava/lang/String;[Ljava/lang/Object;)V
47634795
public fun warn (Ljava/lang/String;[Ljava/lang/Object;)V
@@ -4769,6 +4801,16 @@ public final class io/sentry/logger/NoOpLoggerBatchProcessor : io/sentry/logger/
47694801
public static fun getInstance ()Lio/sentry/logger/NoOpLoggerBatchProcessor;
47704802
}
47714803

4804+
public final class io/sentry/logger/SentryLogParameters {
4805+
public fun <init> ()V
4806+
public static fun create (Lio/sentry/SentryAttributes;)Lio/sentry/logger/SentryLogParameters;
4807+
public static fun create (Lio/sentry/SentryDate;Lio/sentry/SentryAttributes;)Lio/sentry/logger/SentryLogParameters;
4808+
public fun getAttributes ()Lio/sentry/SentryAttributes;
4809+
public fun getTimestamp ()Lio/sentry/SentryDate;
4810+
public fun setAttributes (Lio/sentry/SentryAttributes;)V
4811+
public fun setTimestamp (Lio/sentry/SentryDate;)V
4812+
}
4813+
47724814
public final class io/sentry/opentelemetry/OpenTelemetryUtil {
47734815
public fun <init> ()V
47744816
public static fun applyIgnoredSpanOrigins (Lio/sentry/SentryOptions;)V
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.sentry;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.Nullable;
5+
6+
public final class SentryAttribute {
7+
8+
private final @NotNull String name;
9+
private final @Nullable SentryAttributeType type;
10+
private final @Nullable Object value;
11+
12+
private SentryAttribute(
13+
final @NotNull String name,
14+
final @Nullable SentryAttributeType type,
15+
final @Nullable Object value) {
16+
this.name = name;
17+
this.type = type;
18+
this.value = value;
19+
}
20+
21+
public @NotNull String getName() {
22+
return name;
23+
}
24+
25+
public @Nullable SentryAttributeType getType() {
26+
return type;
27+
}
28+
29+
public @Nullable Object getValue() {
30+
return value;
31+
}
32+
33+
public static @NotNull SentryAttribute named(
34+
final @NotNull String name, final @Nullable Object value) {
35+
return new SentryAttribute(name, null, value);
36+
}
37+
38+
public static @NotNull SentryAttribute booleanAttribute(
39+
final @NotNull String name, final @Nullable Boolean value) {
40+
return new SentryAttribute(name, SentryAttributeType.BOOLEAN, value);
41+
}
42+
43+
public static @NotNull SentryAttribute integerAttribute(
44+
final @NotNull String name, final @Nullable Integer value) {
45+
return new SentryAttribute(name, SentryAttributeType.INTEGER, value);
46+
}
47+
48+
public static @NotNull SentryAttribute doubleAttribute(
49+
final @NotNull String name, final @Nullable Double value) {
50+
return new SentryAttribute(name, SentryAttributeType.DOUBLE, value);
51+
}
52+
53+
public static @NotNull SentryAttribute stringAttribute(
54+
final @NotNull String name, final @Nullable String value) {
55+
return new SentryAttribute(name, SentryAttributeType.STRING, value);
56+
}
57+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.sentry;
2+
3+
import java.util.Locale;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
public enum SentryAttributeType {
7+
STRING,
8+
BOOLEAN,
9+
INTEGER,
10+
DOUBLE;
11+
12+
public @NotNull String apiName() {
13+
return name().toLowerCase(Locale.ROOT);
14+
}
15+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package io.sentry;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.ConcurrentHashMap;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
7+
8+
public final class SentryAttributes {
9+
10+
private final @NotNull Map<String, SentryAttribute> attributes;
11+
12+
private SentryAttributes(final @NotNull Map<String, SentryAttribute> attributes) {
13+
this.attributes = attributes;
14+
}
15+
16+
public void add(final @Nullable SentryAttribute attribute) {
17+
if (attribute == null) {
18+
return;
19+
}
20+
attributes.put(attribute.getName(), attribute);
21+
}
22+
23+
public @NotNull Map<String, SentryAttribute> getAttributes() {
24+
return attributes;
25+
}
26+
27+
public static @NotNull SentryAttributes of(final @Nullable SentryAttribute... attributes) {
28+
if (attributes == null) {
29+
return new SentryAttributes(new ConcurrentHashMap<>());
30+
}
31+
final @NotNull SentryAttributes sentryAttributes =
32+
new SentryAttributes(new ConcurrentHashMap<>(attributes.length));
33+
for (SentryAttribute attribute : attributes) {
34+
sentryAttributes.add(attribute);
35+
}
36+
return sentryAttributes;
37+
}
38+
39+
public static @NotNull SentryAttributes fromMap(final @Nullable Map<String, Object> attributes) {
40+
if (attributes == null) {
41+
return new SentryAttributes(new ConcurrentHashMap<>());
42+
}
43+
final @NotNull SentryAttributes sentryAttributes =
44+
new SentryAttributes(new ConcurrentHashMap<>(attributes.size()));
45+
for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
46+
final @Nullable String key = attribute.getKey();
47+
if (key != null) {
48+
sentryAttributes.add(SentryAttribute.named(key, attribute.getValue()));
49+
}
50+
}
51+
52+
return sentryAttributes;
53+
}
54+
}

sentry/src/main/java/io/sentry/SentryLogEventAttributeValue.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ public final class SentryLogEventAttributeValue implements JsonUnknown, JsonSeri
1515

1616
public SentryLogEventAttributeValue(final @NotNull String type, final @Nullable Object value) {
1717
this.type = type;
18+
if (value != null && type.equals("string")) {
19+
this.value = value.toString();
20+
} else {
21+
this.value = value;
22+
}
23+
}
24+
25+
public SentryLogEventAttributeValue(
26+
final @NotNull SentryAttributeType type, final @Nullable Object value) {
27+
this.type = type.apiName();
1828
this.value = value;
1929
}
2030

sentry/src/main/java/io/sentry/logger/ILoggerApi.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,10 @@ void log(
2828
@Nullable SentryDate timestamp,
2929
@Nullable String message,
3030
@Nullable Object... args);
31+
32+
void log(
33+
@NotNull SentryLogLevel level,
34+
@NotNull SentryLogParameters params,
35+
@Nullable String message,
36+
@Nullable Object... args);
3137
}

sentry/src/main/java/io/sentry/logger/LoggerApi.java

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import io.sentry.ISpan;
66
import io.sentry.PropagationContext;
77
import io.sentry.Scopes;
8+
import io.sentry.SentryAttribute;
9+
import io.sentry.SentryAttributeType;
10+
import io.sentry.SentryAttributes;
811
import io.sentry.SentryDate;
912
import io.sentry.SentryLevel;
1013
import io.sentry.SentryLogEvent;
@@ -65,7 +68,7 @@ public void log(
6568
final @NotNull SentryLogLevel level,
6669
final @Nullable String message,
6770
final @Nullable Object... args) {
68-
log(level, null, message, args);
71+
captureLog(level, SentryLogParameters.create(null, null), message, args);
6972
}
7073

7174
@Override
@@ -74,13 +77,22 @@ public void log(
7477
final @Nullable SentryDate timestamp,
7578
final @Nullable String message,
7679
final @Nullable Object... args) {
77-
captureLog(level, timestamp, message, args);
80+
captureLog(level, SentryLogParameters.create(timestamp, null), message, args);
81+
}
82+
83+
@Override
84+
public void log(
85+
final @NotNull SentryLogLevel level,
86+
final @NotNull SentryLogParameters params,
87+
final @Nullable String message,
88+
final @Nullable Object... args) {
89+
captureLog(level, params, message, args);
7890
}
7991

8092
@SuppressWarnings("AnnotateFormatMethod")
8193
private void captureLog(
8294
final @NotNull SentryLogLevel level,
83-
final @Nullable SentryDate timestamp,
95+
final @NotNull SentryLogParameters params,
8496
final @Nullable String message,
8597
final @Nullable Object... args) {
8698
final @NotNull SentryOptions options = scopes.getOptions();
@@ -103,6 +115,7 @@ private void captureLog(
103115
return;
104116
}
105117

118+
final @Nullable SentryDate timestamp = params.getTimestamp();
106119
final @NotNull SentryDate timestampToUse =
107120
timestamp == null ? options.getDateProvider().now() : timestamp;
108121
final @NotNull String messageToUse = maybeFormatMessage(message, args);
@@ -119,7 +132,7 @@ private void captureLog(
119132
span == null ? propagationContext.getSpanId() : span.getSpanContext().getSpanId();
120133
final SentryLogEvent logEvent =
121134
new SentryLogEvent(traceId, timestampToUse, messageToUse, level);
122-
logEvent.setAttributes(createAttributes(message, spanId, args));
135+
logEvent.setAttributes(createAttributes(params.getAttributes(), message, spanId, args));
123136
logEvent.setSeverityNumber(level.getSeverityNumber());
124137

125138
scopes.getClient().captureLog(logEvent, combinedScope);
@@ -146,12 +159,25 @@ private void captureLog(
146159
}
147160

148161
private @NotNull HashMap<String, SentryLogEventAttributeValue> createAttributes(
149-
final @NotNull String message, final @NotNull SpanId spanId, final @Nullable Object... args) {
162+
final @Nullable SentryAttributes incomingAttributes,
163+
final @NotNull String message,
164+
final @NotNull SpanId spanId,
165+
final @Nullable Object... args) {
150166
final @NotNull HashMap<String, SentryLogEventAttributeValue> attributes = new HashMap<>();
167+
168+
if (incomingAttributes != null) {
169+
for (SentryAttribute attribute : incomingAttributes.getAttributes().values()) {
170+
final @Nullable Object value = attribute.getValue();
171+
final @NotNull SentryAttributeType type =
172+
attribute.getType() == null ? getType(value) : attribute.getType();
173+
attributes.put(attribute.getName(), new SentryLogEventAttributeValue(type, value));
174+
}
175+
}
176+
151177
if (args != null) {
152178
int i = 0;
153179
for (Object arg : args) {
154-
final @NotNull String type = getType(arg);
180+
final @NotNull SentryAttributeType type = getType(arg);
155181
attributes.put(
156182
"sentry.message.parameter." + i, new SentryLogEventAttributeValue(type, arg));
157183
i++;
@@ -205,16 +231,16 @@ private void setServerName(
205231
}
206232
}
207233

208-
private @NotNull String getType(final @Nullable Object arg) {
234+
private @NotNull SentryAttributeType getType(final @Nullable Object arg) {
209235
if (arg instanceof Boolean) {
210-
return "boolean";
236+
return SentryAttributeType.BOOLEAN;
211237
}
212238
if (arg instanceof Integer) {
213-
return "integer";
239+
return SentryAttributeType.INTEGER;
214240
}
215241
if (arg instanceof Number) {
216-
return "double";
242+
return SentryAttributeType.DOUBLE;
217243
}
218-
return "string";
244+
return SentryAttributeType.STRING;
219245
}
220246
}

sentry/src/main/java/io/sentry/logger/NoOpLoggerApi.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,13 @@ public void log(
6161
@Nullable Object... args) {
6262
// do nothing
6363
}
64+
65+
@Override
66+
public void log(
67+
@NotNull SentryLogLevel level,
68+
@NotNull SentryLogParameters params,
69+
@Nullable String message,
70+
@Nullable Object... args) {
71+
// do nothing
72+
}
6473
}

0 commit comments

Comments
 (0)