diff --git a/src/Attributes/Attribute.php b/src/Attributes/Attribute.php index ce792220e..3a79c84c5 100644 --- a/src/Attributes/Attribute.php +++ b/src/Attributes/Attribute.php @@ -4,6 +4,9 @@ namespace Sentry\Attributes; +use Sentry\Serializer\SerializableInterface; +use Sentry\Util\JSON; + /** * @phpstan-type AttributeType 'string'|'boolean'|'integer'|'double' * @phpstan-type AttributeValue string|bool|int|float @@ -68,7 +71,7 @@ public static function fromValue($value): self public static function tryFromValue($value): ?self { if ($value === null) { - return null; + return new self('null', 'string'); } if (\is_bool($value)) { @@ -83,14 +86,22 @@ public static function tryFromValue($value): ?self return new self($value, 'double'); } - if (\is_string($value) || (\is_object($value) && method_exists($value, '__toString'))) { - $stringValue = (string) $value; - - if (empty($stringValue)) { - return null; + if ($value instanceof SerializableInterface) { + try { + return new self(JSON::encode($value->toSentry()), 'string'); + } catch (\Throwable $e) { + // Ignore the exception and continue trying other methods } + } + + if (\is_string($value) || (\is_object($value) && method_exists($value, '__toString'))) { + return new self((string) $value, 'string'); + } - return new self($stringValue, 'string'); + try { + return new self(JSON::encode($value), 'string'); + } catch (\Throwable $e) { + // Ignore the exception } return null; diff --git a/src/Logs/LogsAggregator.php b/src/Logs/LogsAggregator.php index d17b04939..e2fb5ea78 100644 --- a/src/Logs/LogsAggregator.php +++ b/src/Logs/LogsAggregator.php @@ -109,8 +109,6 @@ public function add( $attributes = Arr::simpleDot($attributes); foreach ($attributes as $key => $value) { - $attribute = Attribute::tryFromValue($value); - if (!\is_string($key)) { if ($sdkLogger !== null) { $sdkLogger->info( @@ -121,6 +119,8 @@ public function add( continue; } + $attribute = Attribute::tryFromValue($value); + if ($attribute === null) { if ($sdkLogger !== null) { $sdkLogger->info( diff --git a/tests/Attributes/AttributeTest.php b/tests/Attributes/AttributeTest.php index 030f628bd..1cde9a4bf 100644 --- a/tests/Attributes/AttributeTest.php +++ b/tests/Attributes/AttributeTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; use Sentry\Attributes\Attribute; +use Sentry\Serializer\SerializableInterface; /** * @phpstan-import-type AttributeType from Attribute @@ -43,6 +44,14 @@ public static function fromValueDataProvider(): \Generator ], ]; + yield [ + '', + [ + 'type' => 'string', + 'value' => '', + ], + ]; + yield [ 123, [ @@ -67,6 +76,14 @@ public static function fromValueDataProvider(): \Generator ], ]; + yield [ + null, + [ + 'type' => 'string', + 'value' => 'null', + ], + ]; + yield [ new class { public function __toString(): string @@ -80,19 +97,41 @@ public function __toString(): string ], ]; + yield [ + new class implements SerializableInterface { + public function toSentry(): ?array + { + return ['foo' => 'bar']; + } + }, + [ + 'type' => 'string', + 'value' => '{"foo":"bar"}', + ], + ]; + yield [ new class {}, - null, + [ + 'type' => 'string', + 'value' => '{}', + ], ]; yield [ new \stdClass(), - null, + [ + 'type' => 'string', + 'value' => '{}', + ], ]; yield [ [], - null, + [ + 'type' => 'string', + 'value' => '[]', + ], ]; } @@ -112,6 +151,7 @@ public function testFromValueFactoryMethod(): void { $this->expectException(\InvalidArgumentException::class); - Attribute::fromValue([]); + // Since we support almost any type, we use a resource to trigger the exception + Attribute::fromValue(fopen(__FILE__, 'r')); } } diff --git a/tests/Logs/LogsAggregatorTest.php b/tests/Logs/LogsAggregatorTest.php index 377b9deff..83a91e1da 100644 --- a/tests/Logs/LogsAggregatorTest.php +++ b/tests/Logs/LogsAggregatorTest.php @@ -78,7 +78,7 @@ public static function attributesDataProvider(): \Generator yield [ ['foo' => ['bar']], - [], + ['foo' => '["bar"]'], ]; } diff --git a/tests/Logs/LogsTest.php b/tests/Logs/LogsTest.php index 1917bd48b..3aab97419 100644 --- a/tests/Logs/LogsTest.php +++ b/tests/Logs/LogsTest.php @@ -119,12 +119,17 @@ public function testLogWithNestedAttributes(): void $this->assertNotNull($attribute); $this->assertEquals('bar', $attribute->getValue()); + + $attribute = $logItem->attributes()->get('nested.baz'); + + $this->assertNotNull($attribute); + $this->assertEquals(json_encode([1, 2, 3]), $attribute->getValue()); }); logger()->info('Some message', [], [ 'nested' => [ 'foo' => 'bar', - 'should-be-missing' => [1, 2, 3], + 'baz' => [1, 2, 3], ], ]);