diff --git a/src/Serializer/PayloadSerializer.php b/src/Serializer/PayloadSerializer.php index 09155fce4..c081b5cd5 100644 --- a/src/Serializer/PayloadSerializer.php +++ b/src/Serializer/PayloadSerializer.php @@ -57,6 +57,10 @@ public function serialize(Event $event): string return $this->serializeAsEnvelope($event); } + if ($this->options->isTracingEnabled()) { + return $this->serializeAsEnvelope($event); + } + return $this->serializeAsEvent($event); } @@ -254,12 +258,10 @@ private function serializeAsEnvelope(Event $event): string 'content_type' => 'application/json', ]; - $seralizedEvent = ''; - if (EventType::transaction() === $event->getType()) { - $seralizedEvent = $this->serializeAsEvent($event); - } if (EventType::checkIn() === $event->getType()) { $seralizedEvent = $this->serializeAsCheckInEvent($event); + } else { + $seralizedEvent = $this->serializeAsEvent($event); } return sprintf("%s\n%s\n%s", JSON::encode($envelopeHeader), JSON::encode($itemHeader), $seralizedEvent); diff --git a/src/Transport/HttpTransport.php b/src/Transport/HttpTransport.php index 74186be85..9aa844edb 100644 --- a/src/Transport/HttpTransport.php +++ b/src/Transport/HttpTransport.php @@ -113,6 +113,7 @@ public function send(Event $event): PromiseInterface } if ( + $this->options->isTracingEnabled() || EventType::transaction() === $eventType || EventType::checkIn() === $eventType ) { diff --git a/tests/Serializer/PayloadSerializerTest.php b/tests/Serializer/PayloadSerializerTest.php index afafc33de..5f2523ef7 100644 --- a/tests/Serializer/PayloadSerializerTest.php +++ b/tests/Serializer/PayloadSerializerTest.php @@ -38,31 +38,23 @@ final class PayloadSerializerTest extends TestCase { /** - * @var PayloadSerializer + * @dataProvider serializeAsJsonDataProvider */ - private $serializer; - - protected function setUp(): void + public function testSerializeAsJson(Event $event, string $expectedResult, bool $isOutputJson): void { - $this->serializer = new PayloadSerializer(new Options([ + ClockMock::withClockMock(1597790835); + + $serializer = new PayloadSerializer(new Options([ 'dsn' => 'http://public@example.com/sentry/1', ])); - } - /** - * @dataProvider serializeDataProvider - */ - public function testSerialize(Event $event, string $expectedResult, bool $isOutputJson): void - { - ClockMock::withClockMock(1597790835); - - $result = $this->serializer->serialize($event); + $result = $serializer->serialize($event); if ( EventType::transaction() !== $event->getType() && EventType::checkIn() !== $event->getType() ) { - $resultArray = $this->serializer->toArray($event); + $resultArray = $serializer->toArray($event); $this->assertJsonStringEqualsJsonString($result, json_encode($resultArray)); } @@ -73,7 +65,24 @@ public function testSerialize(Event $event, string $expectedResult, bool $isOutp } } - public function serializeDataProvider(): iterable + /** + * @dataProvider serializeAsEnvelopeDataProvider + */ + public function testSerializeAsEnvelope(Event $event, string $expectedResult): void + { + ClockMock::withClockMock(1597790835); + + $serializer = new PayloadSerializer(new Options([ + 'dsn' => 'http://public@example.com/sentry/1', + 'enable_tracing' => true, + ])); + + $result = $serializer->serialize($event); + + $this->assertSame($expectedResult, $result); + } + + public function serializeAsJsonDataProvider(): iterable { ClockMock::withClockMock(1597790835); @@ -590,4 +599,323 @@ public function serializeDataProvider(): iterable false, ]; } + + public function serializeAsEnvelopeDataProvider(): iterable + { + ClockMock::withClockMock(1597790835); + + $sdkVersion = Client::SDK_VERSION; + + yield [ + Event::createEvent(new EventId('fc9442f5aef34234bb22b9a615e30ccd')), + <<setLevel(Severity::error()); + $event->setLogger('app.php'); + $event->setTransaction('/users//'); + $event->setServerName('foo.example.com'); + $event->setRelease('721e41770371db95eee98ca2707686226b993eda'); + $event->setEnvironment('production'); + $event->setFingerprint(['myrpc', 'POST', '/foo.bar']); + $event->setModules(['my.module.name' => '1.0']); + $event->setStartTimestamp(1597790835); + $event->setBreadcrumb([ + new Breadcrumb(Breadcrumb::LEVEL_INFO, Breadcrumb::TYPE_USER, 'log'), + new Breadcrumb(Breadcrumb::LEVEL_INFO, Breadcrumb::TYPE_NAVIGATION, 'log', null, ['from' => '/login', 'to' => '/dashboard']), + ]); + + $event->setUser(UserDataBag::createFromArray([ + 'id' => 'unique_id', + 'username' => 'my_user', + 'email' => 'foo@example.com', + 'ip_address' => '127.0.0.1', + 'segment' => 'my_segment', + ])); + + $event->setTags([ + 'ios_version' => '4.0', + 'context' => 'production', + ]); + + $event->setExtra([ + 'my_key' => 1, + 'some_other_value' => 'foo bar', + ]); + + $event->setRequest([ + 'method' => 'POST', + 'url' => 'http://absolute.uri/foo', + 'query_string' => 'query=foobar&page=2', + 'data' => [ + 'foo' => 'bar', + ], + 'cookies' => [ + 'PHPSESSID' => '298zf09hf012fh2', + ], + 'headers' => [ + 'content-type' => 'text/html', + ], + 'env' => [ + 'REMOTE_ADDR' => '127.0.0.1', + ], + ]); + + $event->setOsContext(new OsContext( + 'Linux', + '4.19.104-microsoft-standard', + '#1 SMP Wed Feb 19 06:37:35 UTC 2020', + 'Linux 7944782cd697 4.19.104-microsoft-standard #1 SMP Wed Feb 19 06:37:35 UTC 2020 x86_64' + )); + + $event->setRuntimeContext(new RuntimeContext( + 'php', + '7.4.3' + )); + + $event->setContext('electron', [ + 'type' => 'runtime', + 'name' => 'Electron', + 'version' => '4.0', + ]); + + $frame1 = new Frame(null, 'file/name.py', 3); + $frame2 = new Frame('myfunction', 'file/name.py', 3, 'raw_function_name', 'absolute/file/name.py', ['my_var' => 'value'], false); + $frame2->setContextLine(' raise ValueError()'); + $frame2->setPreContext([ + 'def foo():', + ' my_var = \'foo\'', + ]); + + $frame2->setPostContext([ + '', + 'def main():', + ]); + + $event->setExceptions([ + new ExceptionDataBag(new \Exception('initial exception')), + new ExceptionDataBag( + new \Exception('chained exception'), + new Stacktrace([ + $frame1, + $frame2, + ]), + new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, true, ['code' => 123]) + ), + ]); + + yield [ + $event, + <<\/","server_name":"foo.example.com","release":"721e41770371db95eee98ca2707686226b993eda","environment":"production","fingerprint":["myrpc","POST","\/foo.bar"],"modules":{"my.module.name":"1.0"},"extra":{"my_key":1,"some_other_value":"foo bar"},"tags":{"ios_version":"4.0","context":"production"},"user":{"id":"unique_id","username":"my_user","email":"foo@example.com","ip_address":"127.0.0.1","segment":"my_segment"},"contexts":{"os":{"name":"Linux","version":"4.19.104-microsoft-standard","build":"#1 SMP Wed Feb 19 06:37:35 UTC 2020","kernel_version":"Linux 7944782cd697 4.19.104-microsoft-standard #1 SMP Wed Feb 19 06:37:35 UTC 2020 x86_64"},"runtime":{"name":"php","version":"7.4.3"},"electron":{"type":"runtime","name":"Electron","version":"4.0"}},"breadcrumbs":{"values":[{"type":"user","category":"log","level":"info","timestamp":1597790835},{"type":"navigation","category":"log","level":"info","timestamp":1597790835,"data":{"from":"\/login","to":"\/dashboard"}}]},"request":{"method":"POST","url":"http:\/\/absolute.uri\/foo","query_string":"query=foobar&page=2","data":{"foo":"bar"},"cookies":{"PHPSESSID":"298zf09hf012fh2"},"headers":{"content-type":"text\/html"},"env":{"REMOTE_ADDR":"127.0.0.1"}},"exception":{"values":[{"type":"Exception","value":"chained exception","stacktrace":{"frames":[{"filename":"file\/name.py","lineno":3,"in_app":true},{"filename":"file\/name.py","lineno":3,"in_app":false,"abs_path":"absolute\/file\/name.py","function":"myfunction","raw_function":"raw_function_name","pre_context":["def foo():"," my_var = 'foo'"],"context_line":" raise ValueError()","post_context":["","def main():"],"vars":{"my_var":"value"}}]},"mechanism":{"type":"generic","handled":true,"data":{"code":123}}},{"type":"Exception","value":"initial exception"}]}} +TEXT + ]; + + $event = Event::createEvent(new EventId('fc9442f5aef34234bb22b9a615e30ccd')); + $event->setMessage('My raw message with interpreted strings like this', []); + + yield [ + $event, + <<setMessage('My raw message with interpreted strings like %s', ['this']); + + yield [ + $event, + <<setMessage('My raw message with interpreted strings like %s', ['this'], 'My raw message with interpreted strings like that'); + + yield [ + $event, + <<setSpanId(new SpanId('5dd538dc297544cc')); + $span1->setTraceId(new TraceId('21160e9b836d479f81611368b2aa3d2c')); + + $span2 = new Span(); + $span2->setSpanId(new SpanId('b01b9f6349558cd1')); + $span2->setParentSpanId(new SpanId('b0e6f15b45c36b12')); + $span2->setTraceId(new TraceId('1e57b752bc6e4544bbaa246cd1d05dee')); + $span2->setOp('http'); + $span2->setDescription('GET /sockjs-node/info'); + $span2->setStatus(SpanStatus::ok()); + $span2->setStartTimestamp(1597790835); + $span2->setTags(['http.status_code' => '200']); + $span2->setData([ + 'url' => 'http://localhost:8080/sockjs-node/info?t=1588601703755', + 'status_code' => 200, + 'type' => 'xhr', + 'method' => 'GET', + ]); + + $span2->finish(1598659060); + + $event = Event::createTransaction(new EventId('fc9442f5aef34234bb22b9a615e30ccd')); + $event->setSpans([$span1, $span2]); + $event->setRelease('1.0.0'); + $event->setEnvironment('dev'); + $event->setTransaction('GET /'); + $event->setContext('trace', [ + 'trace_id' => '21160e9b836d479f81611368b2aa3d2c', + 'span_id' => '5dd538dc297544cc', + ]); + $event->setRuntimeContext(new RuntimeContext( + 'php', + '8.2.3' + )); + $event->setOsContext(new OsContext( + 'macOS', + '13.2.1', + '22D68', + 'Darwin Kernel Version 22.2.0', + 'aarch64' + )); + + $excimerLog = [ + [ + 'trace' => [ + [ + 'file' => '/var/www/html/index.php', + 'line' => 42, + ], + ], + 'timestamp' => 0.001, + ], + [ + 'trace' => [ + [ + 'file' => '/var/www/html/index.php', + 'line' => 42, + ], + [ + 'class' => 'Function', + 'function' => 'doStuff', + 'file' => '/var/www/html/function.php', + 'line' => 84, + ], + ], + 'timestamp' => 0.002, + ], + ]; + + $profile = new Profile(); + // 2022-02-28T09:41:00Z + $profile->setStartTimeStamp(1677573660.0000); + $profile->setExcimerLog($excimerLog); + $profile->setEventId($event->getId()); + + $event->setSdkMetadata('profile', $profile); + + yield [ + $event, + <<setSdkMetadata('dynamic_sampling_context', DynamicSamplingContext::fromHeader('sentry-public_key=public,sentry-trace_id=d49d9bf66f13450b81f65bc51cf49c03,sentry-sample_rate=1')); + $event->setSdkMetadata('transaction_metadata', new TransactionMetadata()); + + yield [ + $event, + <<setStacktrace(new Stacktrace([new Frame(null, '', 0)])); + + yield [ + $event, + <<setCheckIn($checkIn); + + yield [ + $event, + <<setCheckIn($checkIn); + + yield [ + $event, + <<