Skip to content

Commit 934efa9

Browse files
author
Johann Saunier
committed
Merge pull request #57 from GeniusesOfSymfony/web-profiler
Web profiler
2 parents b8051c8 + 353cb66 commit 934efa9

File tree

9 files changed

+294
-6
lines changed

9 files changed

+294
-6
lines changed

DataCollector/PusherDecorator.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace Gos\Bundle\WebSocketBundle\DataCollector;
4+
5+
use Gos\Bundle\WebSocketBundle\Pusher\PusherInterface;
6+
use Gos\Bundle\PubSubRouterBundle\Request\PubSubRequest;
7+
use Symfony\Component\Stopwatch\Stopwatch;
8+
9+
/**
10+
* This class is only put in front of Pusher in dev env
11+
*/
12+
class PusherDecorator implements PusherInterface
13+
{
14+
/** @var PusherInterface */
15+
protected $pusher;
16+
17+
/**
18+
* Useful when you debug to directly see the decorated class
19+
* @var string
20+
*/
21+
protected $decoratedClass;
22+
23+
/** @var Stopwatch */
24+
protected $stopwatch;
25+
26+
/** @var WebsocketDataCollector */
27+
protected $dataCollector;
28+
29+
/**
30+
* @param PusherInterface $pusher
31+
* @param Stopwatch $stopwatch
32+
* @param WebsocketDataCollector $dataCollector
33+
*/
34+
public function __construct(PusherInterface $pusher, Stopwatch $stopwatch, WebsocketDataCollector $dataCollector)
35+
{
36+
$this->pusher = $pusher;
37+
$this->stopwatch = $stopwatch;
38+
$this->decoratedClass = get_class($pusher);
39+
$this->dataCollector = $dataCollector;
40+
}
41+
42+
/**
43+
* @param string|array $data
44+
* @param string $routeName
45+
* @param array[] $routeParameters
46+
*/
47+
public function push($data, $routeName, Array $routeParameters = array(), Array $context = [])
48+
{
49+
$eventName = 'push.'.$this->getName();
50+
$this->stopwatch->start($eventName, 'websocket');
51+
$this->pusher->push($data, $routeName, $routeParameters, $context);
52+
$this->stopwatch->stop($eventName);
53+
$this->dataCollector->collectData($this->stopwatch->getEvent($eventName), $this->getName());
54+
}
55+
56+
/**
57+
* @return array
58+
*/
59+
public function getConfig()
60+
{
61+
return $this->pusher->getConfig();
62+
}
63+
64+
/**
65+
* @param array $config
66+
*/
67+
public function setConfig($config)
68+
{
69+
$this->pusher->setConfig($config);
70+
}
71+
72+
public function close()
73+
{
74+
$this->pusher->close();
75+
}
76+
77+
/**
78+
* @return string
79+
*/
80+
public function getName()
81+
{
82+
return $this->pusher->getName();
83+
}
84+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
namespace Gos\Bundle\WebSocketBundle\DataCollector;
4+
5+
use Symfony\Component\HttpFoundation\Request;
6+
use Symfony\Component\HttpFoundation\Response;
7+
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
8+
use Symfony\Component\Stopwatch\Stopwatch;
9+
use Symfony\Component\Stopwatch\StopwatchEvent;
10+
11+
class WebsocketDataCollector extends DataCollector
12+
{
13+
/** @var array */
14+
protected $durations;
15+
16+
/** @var [] */
17+
protected $count;
18+
19+
/** @var [] */
20+
protected $rawData;
21+
22+
public function __construct()
23+
{
24+
$this->rawData = [];
25+
}
26+
27+
/**
28+
* Collects data for the given Request and Response.
29+
*
30+
* @param Request $request A Request instance
31+
* @param Response $response A Response instance
32+
* @param \Exception $exception An Exception instance
33+
*
34+
* @api
35+
*/
36+
public function collect(Request $request, Response $response, \Exception $exception = null)
37+
{
38+
$pusherDuration = [];
39+
$pusherCount = [];
40+
$durationTotal = 0;
41+
$totalPush = 0;
42+
43+
foreach($this->rawData as $pusherName => $durations) {
44+
if(!isset($pusherCount[$pusherName])){
45+
$pusherCount[$pusherName] = 0;
46+
}
47+
48+
$pusherDurationTotal = array_sum($durations);
49+
$pusherCount[$pusherName] += $count = count($durations);
50+
$totalPush += $count;
51+
$pusherDuration[$pusherName] = $pusherDurationTotal;
52+
$durationTotal += $pusherDurationTotal;
53+
}
54+
55+
$this->data = [
56+
'pusher_counts' => $pusherCount,
57+
'push_total' => $totalPush,
58+
'durations' => $pusherDuration,
59+
'duration_total' => $durationTotal
60+
];
61+
}
62+
63+
/**
64+
* @return int[]
65+
*/
66+
public function getPusherCounts()
67+
{
68+
return $this->data['pusher_counts'];
69+
}
70+
71+
/**
72+
* @return float
73+
*/
74+
public function getTotalDuration()
75+
{
76+
return $this->data['duration_total'];
77+
}
78+
79+
/**
80+
* @return float[]
81+
*/
82+
public function getDurations()
83+
{
84+
return $this->data['durations'];
85+
}
86+
87+
/**
88+
* @return int
89+
*/
90+
public function getPushTotal()
91+
{
92+
return $this->data['push_total'];
93+
}
94+
95+
/**
96+
* @param StopwatchEvent $event
97+
* @param $pusherName
98+
*/
99+
public function collectData(StopwatchEvent $event, $pusherName)
100+
{
101+
if(!isset($this->rawData[$pusherName])){
102+
$this->rawData[$pusherName] = [];
103+
}
104+
105+
$this->rawData[$pusherName][] = $event->getDuration();
106+
}
107+
108+
/**
109+
* Returns the name of the collector.
110+
*
111+
* @return string The collector name
112+
*
113+
* @api
114+
*/
115+
public function getName()
116+
{
117+
return 'websocket';
118+
}
119+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Gos\Bundle\WebSocketBundle\DependencyInjection\CompilerPass;
4+
5+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
use Symfony\Component\DependencyInjection\Definition;
8+
use Symfony\Component\DependencyInjection\Reference;
9+
10+
class DataCollectorCompilerPass implements CompilerPassInterface
11+
{
12+
/**
13+
* @param ContainerBuilder $container
14+
*/
15+
public function process(ContainerBuilder $container)
16+
{
17+
if (!$container->getParameter('kernel.debug') && $container->has('debug.stopwatch')) {
18+
return;
19+
}
20+
21+
$pushers = $container->findTaggedServiceIds('gos_web_socket.pusher');
22+
23+
foreach ($pushers as $id => $attributes) {
24+
$newPusherId = $id.'.base';
25+
$pusherDef = $container->getDefinition($id);
26+
$container->removeDefinition($id);
27+
$container->setDefinition($newPusherId, $pusherDef);
28+
29+
$container->register($id, 'Gos\Bundle\WebSocketBundle\DataCollector\PusherDecorator')
30+
->addArgument(new Reference($id.'.inner'))
31+
->addArgument(new Reference('debug.stopwatch'))
32+
->addArgument(new Reference('gos_web_socket.data_collector'))
33+
->setDecoratedService($newPusherId)
34+
;
35+
}
36+
}
37+
}

GosWebSocketBundle.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Gos\Bundle\WebSocketBundle;
44

5+
use Gos\Bundle\WebSocketBundle\DependencyInjection\CompilerPass\DataCollectorCompilerPass;
56
use Gos\Bundle\WebSocketBundle\DependencyInjection\CompilerPass\PeriodicCompilerPass;
67
use Gos\Bundle\WebSocketBundle\DependencyInjection\CompilerPass\PingableDriverCompilerPass;
78
use Gos\Bundle\WebSocketBundle\DependencyInjection\CompilerPass\PusherCompilerPass;
@@ -27,6 +28,8 @@ public function build(ContainerBuilder $container)
2728
->addCompilerPass(new TopicCompilerPass())
2829
->addCompilerPass(new PeriodicCompilerPass())
2930
->addCompilerPass(new PingableDriverCompilerPass())
30-
->addCompilerPass(new PusherCompilerPass());
31+
->addCompilerPass(new PusherCompilerPass())
32+
->addCompilerPass(new DataCollectorCompilerPass())
33+
;
3134
}
3235
}

Pusher/Amqp/AmqpPusher.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ protected function doPush($data, array $context)
3333
$this->connection = new \AMQPConnection($config);
3434
$this->connection->connect();
3535

36-
list(, $exchange) = Utils::setupConnection($this->connection, $config);
36+
list(, $this->exchange) = Utils::setupConnection($this->connection, $config);
3737

3838
$this->setConnected();
3939
}
@@ -48,7 +48,7 @@ protected function doPush($data, array $context)
4848

4949
$context = $resolver->resolve($context);
5050

51-
$exchange->publish(
51+
$this->exchange->publish(
5252
$data,
5353
$context['routing_key'],
5454
$context['publish_flags'],

Pusher/Amqp/AmqpServerPushHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class AmqpServerPushHandler extends AbstractServerPushHandler
4545
* @param LoggerInterface|null $logger
4646
*/
4747
public function __construct(
48-
AmqpPusher $pusher,
48+
PusherInterface $pusher,
4949
WampRouter $router,
5050
MessageSerializer $serializer,
5151
EventDispatcherInterface $eventDispatcher,

Pusher/Zmq/ZmqServerPushHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ class ZmqServerPushHandler extends AbstractServerPushHandler
3939
protected $eventDispatcher;
4040

4141
/**
42-
* @param ZmqPusher $pusher
42+
* @param PusherInterface $pusher
4343
* @param WampRouter $router
4444
* @param MessageSerializer $serializer
4545
* @param EventDispatcherInterface $eventDispatcher
4646
* @param LoggerInterface|null $logger
4747
*/
4848
public function __construct(
49-
ZmqPusher $pusher,
49+
PusherInterface $pusher,
5050
WampRouter $router,
5151
MessageSerializer $serializer,
5252
EventDispatcherInterface $eventDispatcher,

Resources/config/services/services.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,8 @@ services:
248248

249249
gos_web_socket.server_push_handler.registry:
250250
class: Gos\Bundle\WebSocketBundle\Pusher\ServerPushHandlerRegistry
251+
252+
gos_web_socket.data_collector:
253+
class: Gos\Bundle\WebSocketBundle\DataCollector\WebsocketDataCollector
254+
tags:
255+
- { name: data_collector, template: 'GosWebSocketBundle:Collector:websocket.html.twig', id: 'websocket' }
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{% extends 'WebProfilerBundle:Profiler:layout.html.twig' %}
2+
3+
{% block toolbar %}
4+
{% set icon %}
5+
<img width="28" height="28" alt="Redis" style="border-width: 0; vertical-align: middle; margin-right: 5px;"
6+
7+
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAACvElEQVR42u2WS2hTQRSG00IR62ORGjUaF7pIYjAJIc8mMdmrUBB8QN24UZC6ERSrK92qK8G9IL4WCkqhoBUEQQpC6cKFCt0UpRRU0L4ErX4/zIXhYpNpbpcdOGTunXPON3POmXMTCq2PtR79/f3hWq12kNF0kUwmsz0QMJfLRQBeaTQaE8hkO6nX63fz+Xw0CLObUybZ/cNms/kb+dtKgC4BHYnFYuEg0K5CoZDA2RPkTzuoNgb0VSqVCgfN524cHeG0J/1SrVbP8/vOioKgI3Z4i8XiHtJznGmPE5AwbSyVSnsrlcp+SSQS2Wyv47AB5K0FXVIqsImzoQNEZ0zvmF8ol8tb2wJRLOLwGU4+StLpdNOvg/NjOP5hhXcO3dvY3We+6L3jpJfR7WsJVNmrEj1n5PW0r6J34fgmOgtWEX3h3SUAQ8ynkGUjs7wb1rVzBvI8jkHVgt1i/bscWsCv6F1X/ojQKeYz5v2y7BW1FYFU3U52dQ/lX15hYPSGfB4FdsMP8xzrNDqludPD1tqkDtEyrHSSGEajdjUqZxguWLCfOhXP8x4U4HvdZ+YnVgXUiMfj24C+XqkRCIbaBhwOIt9M+D4hqY6AGtFoVNBRdZb/dJt51gaTyWQfJzvD87Sp7M6BGtlsVo3gMYZzyGcViBdWnQzoWaNzDnmhuxsIqEEnSeL4DoYXkWsqEKv0pwVThXLRB5AdQYFd6iAAH1AQQ3KsalSBKGf8fkBeojNgulI3uodMPrX+HLuSM00Vi+EYO13kd0r3TKVvvi4picKokwkmm0QiscVbo3HsI0K9zkA1Yu9Omkqc0T1T2FyEDR5mM7HV5K9HjVi90X/hXUSfOjb5lE0mlB4nqLq+OdVsp1By+UhpcIaq6xvouMvfEL8AngB6lZrY5Bxefd3ViF3/bPkFcINm0htaH2s9/gH39f9Ki6w5qQAAAABJRU5ErkJggg=="
8+
9+
10+
>
11+
<span class="sf-toolbar-status">{{ collector.pushTotal }}</span>
12+
{% endset %}
13+
{% set text %}
14+
<div class="sf-toolbar-info-piece">
15+
<b>Push</b>
16+
<span>{{ collector.pushTotal }}</span>
17+
</div>
18+
19+
{% for pusher, duration in collector.durations %}
20+
<div class="sf-toolbar-info-piece">
21+
<b>{{ pusher|upper }} ({{ collector.pusherCounts[pusher] }})</b>
22+
<span>{{ '%0.2f'|format(duration) }} ms</span>
23+
</div>
24+
{% endfor %}
25+
26+
<div class="sf-toolbar-info-piece">
27+
<b>Total time</b>
28+
<span>{{ '%0.2f'|format(collector.totalDuration) }} ms</span>
29+
</div>
30+
{% endset %}
31+
32+
{% set icon %}
33+
<a href="#">{{ icon }}</a>
34+
{% endset %}
35+
36+
<div class="sf-toolbar-block">
37+
<div class="sf-toolbar-icon">{{ icon|default('') }}</div>
38+
<div class="sf-toolbar-info">{{ text|default('') }}</div>
39+
</div>
40+
{% endblock %}

0 commit comments

Comments
 (0)