Skip to content

Commit 6cad7b2

Browse files
authored
feat: try to represent any logs attribute as string (#1950)
1 parent 8e24c11 commit 6cad7b2

File tree

5 files changed

+71
-15
lines changed

5 files changed

+71
-15
lines changed

src/Attributes/Attribute.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
namespace Sentry\Attributes;
66

7+
use Sentry\Serializer\SerializableInterface;
8+
use Sentry\Util\JSON;
9+
710
/**
811
* @phpstan-type AttributeType 'string'|'boolean'|'integer'|'double'
912
* @phpstan-type AttributeValue string|bool|int|float
@@ -68,7 +71,7 @@ public static function fromValue($value): self
6871
public static function tryFromValue($value): ?self
6972
{
7073
if ($value === null) {
71-
return null;
74+
return new self('null', 'string');
7275
}
7376

7477
if (\is_bool($value)) {
@@ -83,14 +86,22 @@ public static function tryFromValue($value): ?self
8386
return new self($value, 'double');
8487
}
8588

86-
if (\is_string($value) || (\is_object($value) && method_exists($value, '__toString'))) {
87-
$stringValue = (string) $value;
88-
89-
if (empty($stringValue)) {
90-
return null;
89+
if ($value instanceof SerializableInterface) {
90+
try {
91+
return new self(JSON::encode($value->toSentry()), 'string');
92+
} catch (\Throwable $e) {
93+
// Ignore the exception and continue trying other methods
9194
}
95+
}
96+
97+
if (\is_string($value) || (\is_object($value) && method_exists($value, '__toString'))) {
98+
return new self((string) $value, 'string');
99+
}
92100

93-
return new self($stringValue, 'string');
101+
try {
102+
return new self(JSON::encode($value), 'string');
103+
} catch (\Throwable $e) {
104+
// Ignore the exception
94105
}
95106

96107
return null;

src/Logs/LogsAggregator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,6 @@ public function add(
109109
$attributes = Arr::simpleDot($attributes);
110110

111111
foreach ($attributes as $key => $value) {
112-
$attribute = Attribute::tryFromValue($value);
113-
114112
if (!\is_string($key)) {
115113
if ($sdkLogger !== null) {
116114
$sdkLogger->info(
@@ -121,6 +119,8 @@ public function add(
121119
continue;
122120
}
123121

122+
$attribute = Attribute::tryFromValue($value);
123+
124124
if ($attribute === null) {
125125
if ($sdkLogger !== null) {
126126
$sdkLogger->info(

tests/Attributes/AttributeTest.php

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use PHPUnit\Framework\TestCase;
88
use Sentry\Attributes\Attribute;
9+
use Sentry\Serializer\SerializableInterface;
910

1011
/**
1112
* @phpstan-import-type AttributeType from Attribute
@@ -43,6 +44,14 @@ public static function fromValueDataProvider(): \Generator
4344
],
4445
];
4546

47+
yield [
48+
'',
49+
[
50+
'type' => 'string',
51+
'value' => '',
52+
],
53+
];
54+
4655
yield [
4756
123,
4857
[
@@ -67,6 +76,14 @@ public static function fromValueDataProvider(): \Generator
6776
],
6877
];
6978

79+
yield [
80+
null,
81+
[
82+
'type' => 'string',
83+
'value' => 'null',
84+
],
85+
];
86+
7087
yield [
7188
new class {
7289
public function __toString(): string
@@ -80,19 +97,41 @@ public function __toString(): string
8097
],
8198
];
8299

100+
yield [
101+
new class implements SerializableInterface {
102+
public function toSentry(): ?array
103+
{
104+
return ['foo' => 'bar'];
105+
}
106+
},
107+
[
108+
'type' => 'string',
109+
'value' => '{"foo":"bar"}',
110+
],
111+
];
112+
83113
yield [
84114
new class {},
85-
null,
115+
[
116+
'type' => 'string',
117+
'value' => '{}',
118+
],
86119
];
87120

88121
yield [
89122
new \stdClass(),
90-
null,
123+
[
124+
'type' => 'string',
125+
'value' => '{}',
126+
],
91127
];
92128

93129
yield [
94130
[],
95-
null,
131+
[
132+
'type' => 'string',
133+
'value' => '[]',
134+
],
96135
];
97136
}
98137

@@ -112,6 +151,7 @@ public function testFromValueFactoryMethod(): void
112151
{
113152
$this->expectException(\InvalidArgumentException::class);
114153

115-
Attribute::fromValue([]);
154+
// Since we support almost any type, we use a resource to trigger the exception
155+
Attribute::fromValue(fopen(__FILE__, 'r'));
116156
}
117157
}

tests/Logs/LogsAggregatorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static function attributesDataProvider(): \Generator
7878

7979
yield [
8080
['foo' => ['bar']],
81-
[],
81+
['foo' => '["bar"]'],
8282
];
8383
}
8484

tests/Logs/LogsTest.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,17 @@ public function testLogWithNestedAttributes(): void
119119

120120
$this->assertNotNull($attribute);
121121
$this->assertEquals('bar', $attribute->getValue());
122+
123+
$attribute = $logItem->attributes()->get('nested.baz');
124+
125+
$this->assertNotNull($attribute);
126+
$this->assertEquals(json_encode([1, 2, 3]), $attribute->getValue());
122127
});
123128

124129
logger()->info('Some message', [], [
125130
'nested' => [
126131
'foo' => 'bar',
127-
'should-be-missing' => [1, 2, 3],
132+
'baz' => [1, 2, 3],
128133
],
129134
]);
130135

0 commit comments

Comments
 (0)