Skip to content

Commit 3e35f04

Browse files
authored
Merge pull request #5124 from getsentry/feat/log-attribute-array-support
feat(core): [Global Attributes 5] Support collections and arrays in log attributes
2 parents e15272c + 8e61d4d commit 3e35f04

File tree

8 files changed

+96
-1
lines changed

8 files changed

+96
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Features
66

7+
- Support collections and arrays in log attribute type inference ([#5124](https://github.com/getsentry/sentry-java/pull/5124))
78
- Add scope-level attributes API ([#5118](https://github.com/getsentry/sentry-java/pull/5118))
89
- Automatically include scope attributes in logs and metrics ([#5120](https://github.com/getsentry/sentry-java/pull/5120))
910
- Create `sentry-opentelemetry-otlp` and `sentry-opentelemetry-otlp-spring` modules for combining OpenTelemetry SDK OTLP export with Sentry SDK ([#5100](https://github.com/getsentry/sentry-java/pull/5100))

sentry/api/sentry.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2813,6 +2813,7 @@ public final class io/sentry/SentryAppStartProfilingOptions$JsonKeys {
28132813
}
28142814

28152815
public final class io/sentry/SentryAttribute {
2816+
public static fun arrayAttribute (Ljava/lang/String;Ljava/util/Collection;)Lio/sentry/SentryAttribute;
28162817
public static fun booleanAttribute (Ljava/lang/String;Ljava/lang/Boolean;)Lio/sentry/SentryAttribute;
28172818
public static fun doubleAttribute (Ljava/lang/String;Ljava/lang/Double;)Lio/sentry/SentryAttribute;
28182819
public fun getName ()Ljava/lang/String;
@@ -2824,6 +2825,7 @@ public final class io/sentry/SentryAttribute {
28242825
}
28252826

28262827
public final class io/sentry/SentryAttributeType : java/lang/Enum {
2828+
public static final field ARRAY Lio/sentry/SentryAttributeType;
28272829
public static final field BOOLEAN Lio/sentry/SentryAttributeType;
28282830
public static final field DOUBLE Lio/sentry/SentryAttributeType;
28292831
public static final field INTEGER Lio/sentry/SentryAttributeType;

sentry/src/main/java/io/sentry/SentryAttribute.java

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

3+
import java.util.Collection;
34
import org.jetbrains.annotations.NotNull;
45
import org.jetbrains.annotations.Nullable;
56

@@ -54,4 +55,9 @@ private SentryAttribute(
5455
final @NotNull String name, final @Nullable String value) {
5556
return new SentryAttribute(name, SentryAttributeType.STRING, value);
5657
}
58+
59+
public static @NotNull SentryAttribute arrayAttribute(
60+
final @NotNull String name, final @Nullable Collection<?> value) {
61+
return new SentryAttribute(name, SentryAttributeType.ARRAY, value);
62+
}
5763
}

sentry/src/main/java/io/sentry/SentryAttributeType.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.sentry;
22

33
import java.math.BigInteger;
4+
import java.util.Collection;
45
import java.util.Locale;
56
import java.util.concurrent.atomic.AtomicInteger;
67
import java.util.concurrent.atomic.AtomicLong;
@@ -11,7 +12,8 @@ public enum SentryAttributeType {
1112
STRING,
1213
BOOLEAN,
1314
INTEGER,
14-
DOUBLE;
15+
DOUBLE,
16+
ARRAY;
1517

1618
public @NotNull String apiName() {
1719
return name().toLowerCase(Locale.ROOT);
@@ -33,6 +35,9 @@ public enum SentryAttributeType {
3335
if (value instanceof Number) {
3436
return DOUBLE;
3537
}
38+
if (value instanceof Collection || (value != null && value.getClass().isArray())) {
39+
return ARRAY;
40+
}
3641
return STRING;
3742
}
3843
}

sentry/src/test/java/io/sentry/ScopesTest.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2770,10 +2770,12 @@ class ScopesTest {
27702770
SentryAttribute.booleanAttribute("boolattr", true),
27712771
SentryAttribute.integerAttribute("intattr", 17),
27722772
SentryAttribute.doubleAttribute("doubleattr", 3.8),
2773+
SentryAttribute.arrayAttribute("arrayattr", listOf("a", "b")),
27732774
SentryAttribute.named("namedstrattr", "namedstrval"),
27742775
SentryAttribute.named("namedboolattr", false),
27752776
SentryAttribute.named("namedintattr", 18),
27762777
SentryAttribute.named("nameddoubleattr", 4.9),
2778+
SentryAttribute.named("namedarrayattr", listOf("x", "y")),
27772779
)
27782780
),
27792781
"log message",
@@ -2802,6 +2804,10 @@ class ScopesTest {
28022804
assertEquals(3.8, doubleattr.value)
28032805
assertEquals("double", doubleattr.type)
28042806

2807+
val arrayattr = it.attributes?.get("arrayattr")!!
2808+
assertEquals(listOf("a", "b"), arrayattr.value)
2809+
assertEquals("array", arrayattr.type)
2810+
28052811
val namedstrattr = it.attributes?.get("namedstrattr")!!
28062812
assertEquals("namedstrval", namedstrattr.value)
28072813
assertEquals("string", namedstrattr.type)
@@ -2817,6 +2823,10 @@ class ScopesTest {
28172823
val nameddoubleattr = it.attributes?.get("nameddoubleattr")!!
28182824
assertEquals(4.9, nameddoubleattr.value)
28192825
assertEquals("double", nameddoubleattr.type)
2826+
2827+
val namedarrayattr = it.attributes?.get("namedarrayattr")!!
2828+
assertEquals(listOf("x", "y"), namedarrayattr.value)
2829+
assertEquals("array", namedarrayattr.type)
28202830
},
28212831
anyOrNull(),
28222832
)
@@ -3504,10 +3514,12 @@ class ScopesTest {
35043514
SentryAttribute.booleanAttribute("boolattr", true),
35053515
SentryAttribute.integerAttribute("intattr", 17),
35063516
SentryAttribute.doubleAttribute("doubleattr", 3.8),
3517+
SentryAttribute.arrayAttribute("arrayattr", listOf("a", "b")),
35073518
SentryAttribute.named("namedstrattr", "namedstrval"),
35083519
SentryAttribute.named("namedboolattr", false),
35093520
SentryAttribute.named("namedintattr", 18),
35103521
SentryAttribute.named("nameddoubleattr", 4.9),
3522+
SentryAttribute.named("namedarrayattr", listOf("x", "y")),
35113523
)
35123524
),
35133525
)
@@ -3536,6 +3548,10 @@ class ScopesTest {
35363548
assertEquals(3.8, doubleattr.value)
35373549
assertEquals("double", doubleattr.type)
35383550

3551+
val arrayattr = it.attributes?.get("arrayattr")!!
3552+
assertEquals(listOf("a", "b"), arrayattr.value)
3553+
assertEquals("array", arrayattr.type)
3554+
35393555
val namedstrattr = it.attributes?.get("namedstrattr")!!
35403556
assertEquals("namedstrval", namedstrattr.value)
35413557
assertEquals("string", namedstrattr.type)
@@ -3551,6 +3567,10 @@ class ScopesTest {
35513567
val nameddoubleattr = it.attributes?.get("nameddoubleattr")!!
35523568
assertEquals(4.9, nameddoubleattr.value)
35533569
assertEquals("double", nameddoubleattr.type)
3570+
3571+
val namedarrayattr = it.attributes?.get("namedarrayattr")!!
3572+
assertEquals(listOf("x", "y"), namedarrayattr.value)
3573+
assertEquals("array", namedarrayattr.type)
35543574
},
35553575
anyOrNull(),
35563576
anyOrNull(),
@@ -3673,10 +3693,12 @@ class ScopesTest {
36733693
SentryAttribute.booleanAttribute("boolattr", true),
36743694
SentryAttribute.integerAttribute("intattr", 17),
36753695
SentryAttribute.doubleAttribute("doubleattr", 3.8),
3696+
SentryAttribute.arrayAttribute("arrayattr", listOf("a", "b")),
36763697
SentryAttribute.named("namedstrattr", "namedstrval"),
36773698
SentryAttribute.named("namedboolattr", false),
36783699
SentryAttribute.named("namedintattr", 18),
36793700
SentryAttribute.named("nameddoubleattr", 4.9),
3701+
SentryAttribute.named("namedarrayattr", listOf("x", "y")),
36803702
)
36813703
),
36823704
)
@@ -3705,6 +3727,10 @@ class ScopesTest {
37053727
assertEquals(3.8, doubleattr.value)
37063728
assertEquals("double", doubleattr.type)
37073729

3730+
val arrayattr = it.attributes?.get("arrayattr")!!
3731+
assertEquals(listOf("a", "b"), arrayattr.value)
3732+
assertEquals("array", arrayattr.type)
3733+
37083734
val namedstrattr = it.attributes?.get("namedstrattr")!!
37093735
assertEquals("namedstrval", namedstrattr.value)
37103736
assertEquals("string", namedstrattr.type)
@@ -3720,6 +3746,10 @@ class ScopesTest {
37203746
val nameddoubleattr = it.attributes?.get("nameddoubleattr")!!
37213747
assertEquals(4.9, nameddoubleattr.value)
37223748
assertEquals("double", nameddoubleattr.type)
3749+
3750+
val namedarrayattr = it.attributes?.get("namedarrayattr")!!
3751+
assertEquals(listOf("x", "y"), namedarrayattr.value)
3752+
assertEquals("array", namedarrayattr.type)
37233753
},
37243754
anyOrNull(),
37253755
anyOrNull(),
@@ -3842,10 +3872,12 @@ class ScopesTest {
38423872
SentryAttribute.booleanAttribute("boolattr", true),
38433873
SentryAttribute.integerAttribute("intattr", 17),
38443874
SentryAttribute.doubleAttribute("doubleattr", 3.8),
3875+
SentryAttribute.arrayAttribute("arrayattr", listOf("a", "b")),
38453876
SentryAttribute.named("namedstrattr", "namedstrval"),
38463877
SentryAttribute.named("namedboolattr", false),
38473878
SentryAttribute.named("namedintattr", 18),
38483879
SentryAttribute.named("nameddoubleattr", 4.9),
3880+
SentryAttribute.named("namedarrayattr", listOf("x", "y")),
38493881
)
38503882
),
38513883
)
@@ -3874,6 +3906,10 @@ class ScopesTest {
38743906
assertEquals(3.8, doubleattr.value)
38753907
assertEquals("double", doubleattr.type)
38763908

3909+
val arrayattr = it.attributes?.get("arrayattr")!!
3910+
assertEquals(listOf("a", "b"), arrayattr.value)
3911+
assertEquals("array", arrayattr.type)
3912+
38773913
val namedstrattr = it.attributes?.get("namedstrattr")!!
38783914
assertEquals("namedstrval", namedstrattr.value)
38793915
assertEquals("string", namedstrattr.type)
@@ -3889,6 +3925,10 @@ class ScopesTest {
38893925
val nameddoubleattr = it.attributes?.get("nameddoubleattr")!!
38903926
assertEquals(4.9, nameddoubleattr.value)
38913927
assertEquals("double", nameddoubleattr.type)
3928+
3929+
val namedarrayattr = it.attributes?.get("namedarrayattr")!!
3930+
assertEquals(listOf("x", "y"), namedarrayattr.value)
3931+
assertEquals("array", namedarrayattr.type)
38923932
},
38933933
anyOrNull(),
38943934
anyOrNull(),

sentry/src/test/java/io/sentry/SentryAttributeTypeTest.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,39 @@ class SentryAttributeTypeTest {
7777
fun `inferFrom returns STRING for null`() {
7878
assertEquals(SentryAttributeType.STRING, SentryAttributeType.inferFrom(null))
7979
}
80+
81+
@Test
82+
fun `inferFrom returns ARRAY for List of Strings`() {
83+
assertEquals(SentryAttributeType.ARRAY, SentryAttributeType.inferFrom(listOf("a", "b")))
84+
}
85+
86+
@Test
87+
fun `inferFrom returns ARRAY for List of Integers`() {
88+
assertEquals(SentryAttributeType.ARRAY, SentryAttributeType.inferFrom(listOf(1, 2, 3)))
89+
}
90+
91+
@Test
92+
fun `inferFrom returns ARRAY for Set of Booleans`() {
93+
assertEquals(SentryAttributeType.ARRAY, SentryAttributeType.inferFrom(setOf(true, false)))
94+
}
95+
96+
@Test
97+
fun `inferFrom returns ARRAY for String array`() {
98+
assertEquals(SentryAttributeType.ARRAY, SentryAttributeType.inferFrom(arrayOf("a", "b")))
99+
}
100+
101+
@Test
102+
fun `inferFrom returns ARRAY for int array`() {
103+
assertEquals(SentryAttributeType.ARRAY, SentryAttributeType.inferFrom(intArrayOf(1, 2)))
104+
}
105+
106+
@Test
107+
fun `inferFrom returns ARRAY for empty list`() {
108+
assertEquals(SentryAttributeType.ARRAY, SentryAttributeType.inferFrom(emptyList<String>()))
109+
}
110+
111+
@Test
112+
fun `inferFrom returns ARRAY for mixed-type list`() {
113+
assertEquals(SentryAttributeType.ARRAY, SentryAttributeType.inferFrom(listOf("a", 1, true)))
114+
}
80115
}

sentry/src/test/java/io/sentry/protocol/SentryLogsSerializationTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class SentryLogsSerializationTest {
3838
"sentry.sdk.name" to
3939
SentryLogEventAttributeValue("string", "sentry.java.spring-boot.jakarta"),
4040
"sentry.environment" to SentryLogEventAttributeValue("string", "production"),
41+
"custom.array" to SentryLogEventAttributeValue("array", listOf("a", "b")),
4142
"sentry.sdk.version" to SentryLogEventAttributeValue("string", "8.11.1"),
4243
"sentry.trace.parent_span_id" to
4344
SentryLogEventAttributeValue("string", "f28b86350e534671"),

sentry/src/test/resources/json/sentry_logs.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
"type": "string",
2121
"value": "production"
2222
},
23+
"custom.array":
24+
{
25+
"type": "array",
26+
"value": ["a", "b"]
27+
},
2328
"sentry.sdk.version":
2429
{
2530
"type": "string",

0 commit comments

Comments
 (0)