Skip to content

Commit 2ce5d46

Browse files
committed
feature #27202 [Messenger] Improve the profiler panel (ogizanagi)
This PR was squashed before being merged into the 4.1 branch (closes #27202). Discussion ---------- [Messenger] Improve the profiler panel | Q | A | ------------- | --- | Branch? | 4.1 <!-- see below --> | Bug fix? | no | New feature? | yes <!-- don't forget to update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | N/A <!-- #26597 issue number(s), if any --> | License | MIT | Doc PR | N/A This is an attempt to enhance the profiler panel a bit. **with the following messages dispatched:** ```php $queryBus->dispatch(Envelope::wrap(new GetGreetingsQuery('Hello you')) ->with(new JustAFriendOfMine()) ->with(new AndHisPlusO̶n̶e̶Eleven()) ); $commandBus->dispatch(new SendGiftCommand()); $queryBus->dispatch(new GetGreetingsQuery('Exterminate!')); ``` ## Before <img width="1084" alt="screenshot 2018-05-12 a 13 57 57" src="https://user-images.githubusercontent.com/2211145/39957055-8a0f009e-55ec-11e8-9d8e-bf79aad4b420.PNG"> 🐛calls order are wrong here, fixed in this PR ## After ### collapsed <!-- <img width="1083" alt="screenshot 2018-05-10 a 23 51 07" src="https://user-images.githubusercontent.com/2211145/39896093-19a8c7ee-54ad-11e8-8dcb-4e165ffd2eae.PNG">--> <img width="1085" alt="screenshot 2018-05-12 a 13 18 35" src="https://user-images.githubusercontent.com/2211145/39956802-9d4c38a2-55e7-11e8-8425-ad090c0871b6.PNG"> <img width="1085" alt="screenshot 2018-05-12 a 13 26 44" src="https://user-images.githubusercontent.com/2211145/39956827-25d9e426-55e8-11e8-9116-160603649f33.PNG"> 📝 _When loading the page, all messages details are collapsed by default but the first one per tab._ ### expanded <!-- <img width="1083" alt="screenshot 2018-05-10 a 23 13 39" src="https://user-images.githubusercontent.com/2211145/39894779-42c9cc9a-54a8-11e8-9529-6292481536d4.PNG"> --> <img width="1086" alt="screenshot 2018-05-12 a 13 49 42" src="https://user-images.githubusercontent.com/2211145/39956981-639fc3d6-55eb-11e8-9224-a48f591db3da.PNG"> ### live <!-- ![mai-10-2018 23-16-17](https://user-images.githubusercontent.com/2211145/39894789-4b8fa138-54a8-11e8-986c-fccf6cd0234f.gif) --> ![mai-12-2018 13-55-40](https://user-images.githubusercontent.com/2211145/39957041-37f17b34-55ec-11e8-8569-a733a104bf82.gif) ### toolbar (with exceptions) <img width="284" alt="screenshot 2018-05-10 a 23 18 32" src="https://user-images.githubusercontent.com/2211145/39895011-0467f2a0-54a9-11e8-9d78-25461cf71c41.PNG"> ## Notes - Table headers are clickable, so you can jump directly to the message class in the code - Reversing headers/rows allows to have a wider space for dumps and allows to add more entries in the future. This is an issue we already have with the Validator panel (when there is both an invalid value as object and a constraint violation dumped) which I'd like to revamp soon. - ~~I wonder if we should keep the dispatched messages in call order, or if we can segregate by bus (using tabs?).~~ - ~~we could add a left container listing messages classes only, allowing to show details of a single message dispatched on a right container (similar to what the Form panel does). I'll probably suggest the same for the Validator panel.~~ Commits ------- 3d19578297 [Messenger] Improve the profiler panel
2 parents 5660d2b + 29733e4 commit 2ce5d46

File tree

6 files changed

+174
-64
lines changed

6 files changed

+174
-64
lines changed

DataCollector/MessengerDataCollector.php

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
1717
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
1818
use Symfony\Component\Messenger\TraceableMessageBus;
19+
use Symfony\Component\VarDumper\Caster\ClassStub;
20+
use Symfony\Component\VarDumper\Cloner\Data;
1921

2022
/**
2123
* @author Samuel Roze <[email protected]>
@@ -44,13 +46,25 @@ public function collect(Request $request, Response $response, \Exception $except
4446
*/
4547
public function lateCollect()
4648
{
47-
$this->data = array('messages' => array());
49+
$this->data = array('messages' => array(), 'buses' => array_keys($this->traceableBuses));
4850

51+
$messages = array();
4952
foreach ($this->traceableBuses as $busName => $bus) {
5053
foreach ($bus->getDispatchedMessages() as $message) {
51-
$this->data['messages'][] = $this->collectMessage($busName, $message);
54+
$debugRepresentation = $this->cloneVar($this->collectMessage($busName, $message));
55+
$messages[] = array($debugRepresentation, $message['callTime']);
5256
}
5357
}
58+
59+
// Order by call time
60+
usort($messages, function (array $a, array $b): int {
61+
return $a[1] > $b[1] ? 1 : -1;
62+
});
63+
64+
// Keep the messages clones only
65+
$this->data['messages'] = array_map(function (array $item): Data {
66+
return $item[0];
67+
}, $messages);
5468
}
5569

5670
/**
@@ -78,47 +92,51 @@ private function collectMessage(string $busName, array $tracedMessage)
7892

7993
$debugRepresentation = array(
8094
'bus' => $busName,
95+
'envelopeItems' => $tracedMessage['envelopeItems'] ?? null,
8196
'message' => array(
82-
'type' => \get_class($message),
83-
'object' => $this->cloneVar($message),
97+
'type' => new ClassStub(\get_class($message)),
98+
'value' => $message,
8499
),
85100
);
86101

87102
if (array_key_exists('result', $tracedMessage)) {
88103
$result = $tracedMessage['result'];
89-
90-
if (\is_object($result)) {
91-
$debugRepresentation['result'] = array(
92-
'type' => \get_class($result),
93-
'object' => $this->cloneVar($result),
94-
);
95-
} elseif (\is_array($result)) {
96-
$debugRepresentation['result'] = array(
97-
'type' => 'array',
98-
'object' => $this->cloneVar($result),
99-
);
100-
} else {
101-
$debugRepresentation['result'] = array(
102-
'type' => \gettype($result),
103-
'value' => $result,
104-
);
105-
}
104+
$debugRepresentation['result'] = array(
105+
'type' => \is_object($result) ? \get_class($result) : gettype($result),
106+
'value' => $result,
107+
);
106108
}
107109

108110
if (isset($tracedMessage['exception'])) {
109111
$exception = $tracedMessage['exception'];
110112

111113
$debugRepresentation['exception'] = array(
112114
'type' => \get_class($exception),
113-
'message' => $exception->getMessage(),
115+
'value' => $exception,
114116
);
115117
}
116118

117119
return $debugRepresentation;
118120
}
119121

120-
public function getMessages(): array
122+
public function getExceptionsCount(string $bus = null): int
123+
{
124+
return array_reduce($this->getMessages($bus), function (int $carry, Data $message) {
125+
return $carry += isset($message['exception']) ? 1 : 0;
126+
}, 0);
127+
}
128+
129+
public function getMessages(string $bus = null): array
130+
{
131+
$messages = $this->data['messages'] ?? array();
132+
133+
return $bus ? array_filter($messages, function (Data $message) use ($bus): bool {
134+
return $bus === $message['bus'];
135+
}) : $messages;
136+
}
137+
138+
public function getBuses(): array
121139
{
122-
return $this->data['messages'] ?? array();
140+
return $this->data['buses'];
123141
}
124142
}

Tests/DataCollector/MessengerDataCollectorTest.php

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,22 @@
1616
use Symfony\Component\Messenger\MessageBusInterface;
1717
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
1818
use Symfony\Component\Messenger\TraceableMessageBus;
19-
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
19+
use Symfony\Component\VarDumper\Cloner\Data;
20+
use Symfony\Component\VarDumper\Dumper\CliDumper;
2021

2122
/**
2223
* @author Maxime Steinhausser <[email protected]>
2324
*/
2425
class MessengerDataCollectorTest extends TestCase
2526
{
26-
use VarDumperTestTrait;
27+
/** @var CliDumper */
28+
private $dumper;
29+
30+
protected function setUp()
31+
{
32+
$this->dumper = new CliDumper();
33+
$this->dumper->setColors(false);
34+
}
2735

2836
/**
2937
* @dataProvider getHandleTestData
@@ -46,25 +54,26 @@ public function testHandle($returnedValue, $expected)
4654
$messages = $collector->getMessages();
4755
$this->assertCount(1, $messages);
4856

49-
$this->assertDumpMatchesFormat($expected, $messages[0]);
57+
$this->assertStringMatchesFormat($expected, $this->getDataAsString($messages[0]));
5058
}
5159

5260
public function getHandleTestData()
5361
{
5462
$messageDump = <<<DUMP
5563
"bus" => "default"
64+
"envelopeItems" => null
5665
"message" => array:2 [
5766
"type" => "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage"
58-
"object" => Symfony\Component\VarDumper\Cloner\Data {%A
59-
%A+class: "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage"%A
67+
"value" => Symfony\Component\Messenger\Tests\Fixtures\DummyMessage %A
68+
-message: "dummy message"
6069
}
6170
]
6271
DUMP;
6372

6473
yield 'no returned value' => array(
6574
null,
6675
<<<DUMP
67-
array:3 [
76+
array:4 [
6877
$messageDump
6978
"result" => array:2 [
7079
"type" => "NULL"
@@ -77,7 +86,7 @@ public function getHandleTestData()
7786
yield 'scalar returned value' => array(
7887
'returned value',
7988
<<<DUMP
80-
array:3 [
89+
array:4 [
8190
$messageDump
8291
"result" => array:2 [
8392
"type" => "string"
@@ -90,11 +99,13 @@ public function getHandleTestData()
9099
yield 'array returned value' => array(
91100
array('returned value'),
92101
<<<DUMP
93-
array:3 [
102+
array:4 [
94103
$messageDump
95104
"result" => array:2 [
96105
"type" => "array"
97-
"object" => Symfony\Component\VarDumper\Cloner\Data {%A
106+
"value" => array:1 [
107+
0 => "returned value"
108+
]
98109
]
99110
]
100111
DUMP
@@ -123,21 +134,66 @@ public function testHandleWithException()
123134
$messages = $collector->getMessages();
124135
$this->assertCount(1, $messages);
125136

126-
$this->assertDumpMatchesFormat(<<<DUMP
127-
array:3 [
137+
$this->assertStringMatchesFormat(<<<DUMP
138+
array:4 [
128139
"bus" => "default"
140+
"envelopeItems" => null
129141
"message" => array:2 [
130142
"type" => "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage"
131-
"object" => Symfony\Component\VarDumper\Cloner\Data {%A
132-
%A+class: "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage"%A
143+
"value" => Symfony\Component\Messenger\Tests\Fixtures\DummyMessage %A
144+
-message: "dummy message"
133145
}
134146
]
135147
"exception" => array:2 [
136148
"type" => "RuntimeException"
137-
"message" => "foo"
149+
"value" => RuntimeException %A
138150
]
139-
]
151+
]
140152
DUMP
141-
, $messages[0]);
153+
, $this->getDataAsString($messages[0]));
154+
}
155+
156+
public function testKeepsOrderedDispatchCalls()
157+
{
158+
$firstBus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
159+
$firstBus = new TraceableMessageBus($firstBus);
160+
161+
$secondBus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
162+
$secondBus = new TraceableMessageBus($secondBus);
163+
164+
$collector = new MessengerDataCollector();
165+
$collector->registerBus('first bus', $firstBus);
166+
$collector->registerBus('second bus', $secondBus);
167+
168+
$firstBus->dispatch(new DummyMessage('#1'));
169+
$secondBus->dispatch(new DummyMessage('#2'));
170+
$secondBus->dispatch(new DummyMessage('#3'));
171+
$firstBus->dispatch(new DummyMessage('#4'));
172+
$secondBus->dispatch(new DummyMessage('#5'));
173+
174+
$collector->lateCollect();
175+
176+
$messages = $collector->getMessages();
177+
$this->assertCount(5, $messages);
178+
179+
$this->assertSame('#1', $messages[0]['message']['value']['message']);
180+
$this->assertSame('first bus', $messages[0]['bus']);
181+
182+
$this->assertSame('#2', $messages[1]['message']['value']['message']);
183+
$this->assertSame('second bus', $messages[1]['bus']);
184+
185+
$this->assertSame('#3', $messages[2]['message']['value']['message']);
186+
$this->assertSame('second bus', $messages[2]['bus']);
187+
188+
$this->assertSame('#4', $messages[3]['message']['value']['message']);
189+
$this->assertSame('first bus', $messages[3]['bus']);
190+
191+
$this->assertSame('#5', $messages[4]['message']['value']['message']);
192+
$this->assertSame('second bus', $messages[4]['bus']);
193+
}
194+
195+
private function getDataAsString(Data $data): string
196+
{
197+
return rtrim($this->dumper->dump($data, true));
142198
}
143199
}

Tests/Fixtures/AnEnvelopeItem.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Tests\Fixtures;
13+
14+
use Symfony\Component\Messenger\EnvelopeItemInterface;
15+
16+
class AnEnvelopeItem implements EnvelopeItemInterface
17+
{
18+
/**
19+
* {@inheritdoc}
20+
*/
21+
public function serialize()
22+
{
23+
return '';
24+
}
25+
26+
/**
27+
* {@inheritdoc}
28+
*/
29+
public function unserialize($serialized)
30+
{
31+
// noop
32+
}
33+
}

Tests/MessageBusTest.php

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
use Symfony\Component\Messenger\Asynchronous\Transport\ReceivedMessage;
1616
use Symfony\Component\Messenger\Envelope;
1717
use Symfony\Component\Messenger\EnvelopeAwareInterface;
18-
use Symfony\Component\Messenger\EnvelopeItemInterface;
1918
use Symfony\Component\Messenger\MessageBus;
2019
use Symfony\Component\Messenger\MessageBusInterface;
2120
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
21+
use Symfony\Component\Messenger\Tests\Fixtures\AnEnvelopeItem;
2222
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
2323

2424
class MessageBusTest extends TestCase
@@ -160,22 +160,3 @@ public function testThatAMiddlewareCanUpdateTheMessageWhileKeepingTheEnvelopeIte
160160
$bus->dispatch($envelope);
161161
}
162162
}
163-
164-
class AnEnvelopeItem implements EnvelopeItemInterface
165-
{
166-
/**
167-
* {@inheritdoc}
168-
*/
169-
public function serialize()
170-
{
171-
return '';
172-
}
173-
174-
/**
175-
* {@inheritdoc}
176-
*/
177-
public function unserialize($serialized)
178-
{
179-
return new self();
180-
}
181-
}

Tests/TraceableMessageBusTest.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Messenger\Envelope;
1616
use Symfony\Component\Messenger\MessageBusInterface;
17+
use Symfony\Component\Messenger\Tests\Fixtures\AnEnvelopeItem;
1718
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
1819
use Symfony\Component\Messenger\TraceableMessageBus;
1920

@@ -28,19 +29,29 @@ public function testItTracesResult()
2829

2930
$traceableBus = new TraceableMessageBus($bus);
3031
$this->assertSame($result, $traceableBus->dispatch($message));
31-
$this->assertSame(array(array('message' => $message, 'result' => $result)), $traceableBus->getDispatchedMessages());
32+
$this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages());
33+
$this->assertArraySubset(array(
34+
'message' => $message,
35+
'result' => $result,
36+
'envelopeItems' => null,
37+
), $tracedMessages[0], true);
3238
}
3339

3440
public function testItTracesResultWithEnvelope()
3541
{
36-
$envelope = Envelope::wrap($message = new DummyMessage('Hello'));
42+
$envelope = Envelope::wrap($message = new DummyMessage('Hello'))->with($envelopeItem = new AnEnvelopeItem());
3743

3844
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
3945
$bus->expects($this->once())->method('dispatch')->with($envelope)->willReturn($result = array('foo' => 'bar'));
4046

4147
$traceableBus = new TraceableMessageBus($bus);
4248
$this->assertSame($result, $traceableBus->dispatch($envelope));
43-
$this->assertSame(array(array('message' => $message, 'result' => $result)), $traceableBus->getDispatchedMessages());
49+
$this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages());
50+
$this->assertArraySubset(array(
51+
'message' => $message,
52+
'result' => $result,
53+
'envelopeItems' => array($envelopeItem),
54+
), $tracedMessages[0], true);
4455
}
4556

4657
public function testItTracesExceptions()
@@ -58,6 +69,11 @@ public function testItTracesExceptions()
5869
$this->assertSame($exception, $e);
5970
}
6071

61-
$this->assertSame(array(array('message' => $message, 'exception' => $exception)), $traceableBus->getDispatchedMessages());
72+
$this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages());
73+
$this->assertArraySubset(array(
74+
'message' => $message,
75+
'exception' => $exception,
76+
'envelopeItems' => null,
77+
), $tracedMessages[0], true);
6278
}
6379
}

0 commit comments

Comments
 (0)