[HttpKernel] decoupled TraceableEventDispatcher and Profiler
This commit is contained in:
parent
59409b47c8
commit
9c4bc9a0ed
@ -34,6 +34,7 @@
|
||||
|
||||
<service id="data_collector.events" class="%data_collector.events.class%" public="false">
|
||||
<tag name="data_collector" template="@WebProfiler/Collector/events.html.twig" id="events" priority="255" />
|
||||
<argument type="service" id="event_dispatcher" on-invalid="ignore" />
|
||||
</service>
|
||||
|
||||
<service id="data_collector.logger" class="%data_collector.logger.class%" public="false">
|
||||
@ -45,6 +46,7 @@
|
||||
<service id="data_collector.time" class="%data_collector.time.class%" public="false">
|
||||
<tag name="data_collector" template="@WebProfiler/Collector/time.html.twig" id="time" priority="255" />
|
||||
<argument type="service" id="kernel" on-invalid="ignore" />
|
||||
<argument type="service" id="debug.stopwatch" on-invalid="ignore" />
|
||||
</service>
|
||||
|
||||
<service id="data_collector.memory" class="%data_collector.memory.class%" public="false">
|
||||
|
@ -19,7 +19,6 @@
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
<argument type="service" id="debug.stopwatch" />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
<call method="setProfiler"><argument type="service" id="profiler" on-invalid="null" /></call>
|
||||
</service>
|
||||
|
||||
<service id="debug.controller_resolver" class="%debug.controller_resolver.class%">
|
||||
|
@ -29,6 +29,7 @@
|
||||
<argument type="service" id="profiler.request_matcher" on-invalid="null" />
|
||||
<argument>%profiler_listener.only_exceptions%</argument>
|
||||
<argument>%profiler_listener.only_master_requests%</argument>
|
||||
<argument type="service" id="request_stack" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface;
|
||||
|
||||
/**
|
||||
@ -22,6 +23,13 @@ use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface;
|
||||
*/
|
||||
class EventDataCollector extends DataCollector
|
||||
{
|
||||
protected $dispatcher;
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher = null)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -81,6 +89,16 @@ class EventDataCollector extends DataCollector
|
||||
return $this->data['not_called_listeners'];
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
if ($this->dispatcher instanceof TraceableEventDispatcherInterface) {
|
||||
$this->setCalledListeners($this->dispatcher->getCalledListeners());
|
||||
$this->setNotCalledListeners($this->dispatcher->getNotCalledListeners());
|
||||
}
|
||||
|
||||
return parent::serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -65,6 +65,13 @@ class MemoryDataCollector extends DataCollector
|
||||
$this->data['memory'] = memory_get_peak_usage(true);
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
$this->updateMemoryUsage();
|
||||
|
||||
return parent::serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -15,6 +15,7 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
|
||||
/**
|
||||
* TimeDataCollector.
|
||||
@ -24,10 +25,12 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
class TimeDataCollector extends DataCollector
|
||||
{
|
||||
protected $kernel;
|
||||
protected $stopwatch;
|
||||
|
||||
public function __construct(KernelInterface $kernel = null)
|
||||
public function __construct(KernelInterface $kernel = null, $stopwatch = null)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
$this->stopwatch = $stopwatch;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,6 +45,7 @@ class TimeDataCollector extends DataCollector
|
||||
}
|
||||
|
||||
$this->data = array(
|
||||
'token' => $response->headers->get('X-Debug-Token'),
|
||||
'start_time' => $startTime * 1000,
|
||||
'events' => array(),
|
||||
);
|
||||
@ -113,6 +117,16 @@ class TimeDataCollector extends DataCollector
|
||||
return $this->data['start_time'];
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
if (null !== $this->stopwatch && isset($this->data['token'])) {
|
||||
$this->setEvents($this->stopwatch->getSectionEvents($this->data['token']));
|
||||
}
|
||||
unset($this->data['token']);
|
||||
|
||||
return parent::serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -14,8 +14,6 @@ namespace Symfony\Component\HttpKernel\Debug;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
@ -33,7 +31,6 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
|
||||
private $logger;
|
||||
private $called;
|
||||
private $stopwatch;
|
||||
private $profiler;
|
||||
private $dispatcher;
|
||||
private $wrappedListeners;
|
||||
private $firstCalledEvent;
|
||||
@ -59,11 +56,16 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
|
||||
/**
|
||||
* Sets the profiler.
|
||||
*
|
||||
* The traceable event dispatcher does not use the profiler anymore.
|
||||
* The job is now done directly by the Profiler listener and the
|
||||
* data collectors themselves.
|
||||
*
|
||||
* @param Profiler|null $profiler A Profiler instance
|
||||
*
|
||||
* @deprecated Deprecated since version 2.4, to be removed in 3.0.
|
||||
*/
|
||||
public function setProfiler(Profiler $profiler = null)
|
||||
{
|
||||
$this->profiler = $profiler;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +144,9 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
|
||||
|
||||
unset($this->firstCalledEvent[$eventName]);
|
||||
|
||||
if ($e->isStarted()) {
|
||||
$e->stop();
|
||||
}
|
||||
|
||||
$this->postDispatch($eventName, $event);
|
||||
|
||||
@ -312,57 +316,6 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the stopwatch data in the profile hierarchy.
|
||||
*
|
||||
* @param string $token Profile token
|
||||
* @param Boolean $updateChildren Whether to update the children altogether
|
||||
*/
|
||||
private function updateProfiles($token, $updateChildren)
|
||||
{
|
||||
if (!$this->profiler || !$profile = $this->profiler->loadProfile($token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->saveInfoInProfile($profile, $updateChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the profiles with the timing and events information and saves them.
|
||||
*
|
||||
* @param Profile $profile The root profile
|
||||
* @param Boolean $updateChildren Whether to update the children altogether
|
||||
*/
|
||||
private function saveInfoInProfile(Profile $profile, $updateChildren)
|
||||
{
|
||||
try {
|
||||
$collector = $profile->getCollector('memory');
|
||||
$collector->updateMemoryUsage();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
$collector = $profile->getCollector('time');
|
||||
$collector->setEvents($this->stopwatch->getSectionEvents($profile->getToken()));
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
$collector = $profile->getCollector('events');
|
||||
$collector->setCalledListeners($this->getCalledListeners());
|
||||
$collector->setNotCalledListeners($this->getNotCalledListeners());
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
}
|
||||
|
||||
$this->profiler->saveProfile($profile);
|
||||
|
||||
if ($updateChildren) {
|
||||
foreach ($profile->getChildren() as $child) {
|
||||
$this->saveInfoInProfile($child, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function preDispatch($eventName, Event $event)
|
||||
{
|
||||
// wrap all listeners before they are called
|
||||
@ -411,23 +364,14 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
|
||||
case KernelEvents::RESPONSE:
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
$this->stopwatch->stopSection($token);
|
||||
if ($event->isMasterRequest()) {
|
||||
// The profiles can only be updated once they have been created
|
||||
// that is after the 'kernel.response' event of the main request
|
||||
$this->updateProfiles($token, true);
|
||||
}
|
||||
break;
|
||||
case KernelEvents::TERMINATE:
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
// In the special case described in the `preDispatch` method above, the `$token` section
|
||||
// does not exist, then closing it throws an exception which must be caught.
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
try {
|
||||
$this->stopwatch->stopSection($token);
|
||||
} catch (\LogicException $e) {}
|
||||
// The children profiles have been updated by the previous 'kernel.response'
|
||||
// event. Only the root profile need to be updated with the 'kernel.terminate'
|
||||
// timing informations.
|
||||
$this->updateProfiles($token, false);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -448,7 +392,9 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
|
||||
|
||||
call_user_func($listener, $event, $eventName, $self);
|
||||
|
||||
if ($e->isStarted()) {
|
||||
$e->stop();
|
||||
}
|
||||
|
||||
if ($event->isPropagationStopped()) {
|
||||
$self->logSkippedListeners($eventName, $event, $listener);
|
||||
|
@ -11,13 +11,16 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
@ -32,9 +35,10 @@ class ProfilerListener implements EventSubscriberInterface
|
||||
protected $onlyException;
|
||||
protected $onlyMasterRequests;
|
||||
protected $exception;
|
||||
protected $children;
|
||||
protected $requests;
|
||||
protected $profiles;
|
||||
protected $requestStack;
|
||||
protected $parents;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -44,14 +48,16 @@ class ProfilerListener implements EventSubscriberInterface
|
||||
* @param Boolean $onlyException true if the profiler only collects data when an exception occurs, false otherwise
|
||||
* @param Boolean $onlyMasterRequests true if the profiler only collects data when the request is a master request, false otherwise
|
||||
*/
|
||||
public function __construct(Profiler $profiler, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false)
|
||||
public function __construct(Profiler $profiler, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false, RequestStack $requestStack = null)
|
||||
{
|
||||
$this->profiler = $profiler;
|
||||
$this->matcher = $matcher;
|
||||
$this->onlyException = (Boolean) $onlyException;
|
||||
$this->onlyMasterRequests = (Boolean) $onlyMasterRequests;
|
||||
$this->children = new \SplObjectStorage();
|
||||
$this->profiles = array();
|
||||
$this->profiles = new \SplObjectStorage();
|
||||
$this->parents = new \SplObjectStorage();
|
||||
$this->requests = array();
|
||||
$this->requestStack = $requestStack;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,10 +74,15 @@ class ProfilerListener implements EventSubscriberInterface
|
||||
$this->exception = $event->getException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated since version 2.4, to be removed in 3.0.
|
||||
*/
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
if (null === $this->requestStack) {
|
||||
$this->requests[] = $event->getRequest();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the onKernelResponse event.
|
||||
@ -101,42 +112,35 @@ class ProfilerListener implements EventSubscriberInterface
|
||||
return;
|
||||
}
|
||||
|
||||
$this->profiles[] = $profile;
|
||||
$this->profiles[$request] = $profile;
|
||||
|
||||
if (null !== $exception) {
|
||||
foreach ($this->profiles as $profile) {
|
||||
$this->profiler->saveProfile($profile);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// keep the profile as the child of its parent
|
||||
if (!$master) {
|
||||
if (null !== $this->requestStack) {
|
||||
$this->parents[$request] = $this->requestStack->getParentRequest();
|
||||
} elseif (!$master) {
|
||||
// to be removed when requestStack is required
|
||||
array_pop($this->requests);
|
||||
|
||||
$parent = end($this->requests);
|
||||
|
||||
// when simulating requests, we might not have the parent
|
||||
if ($parent) {
|
||||
$profiles = isset($this->children[$parent]) ? $this->children[$parent] : array();
|
||||
$profiles[] = $profile;
|
||||
$this->children[$parent] = $profiles;
|
||||
$this->parents[$request] = end($this->requests);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->children[$request])) {
|
||||
foreach ($this->children[$request] as $child) {
|
||||
$profile->addChild($child);
|
||||
public function onKernelTerminate(PostResponseEvent $event)
|
||||
{
|
||||
// attach children to parents
|
||||
foreach ($this->profiles as $request) {
|
||||
if ($parentRequest = $this->parents[$request]) {
|
||||
$this->profiles[$parentRequest]->addChild($this->profiles[$request]);
|
||||
}
|
||||
$this->children[$request] = array();
|
||||
}
|
||||
|
||||
if ($master) {
|
||||
$this->saveProfiles($profile);
|
||||
|
||||
$this->children = new \SplObjectStorage();
|
||||
// save profiles
|
||||
foreach ($this->profiles as $request) {
|
||||
$this->profiler->saveProfile($this->profiles[$request]);
|
||||
}
|
||||
|
||||
$this->profiles = new \SplObjectStorage();
|
||||
$this->parents = new \SplObjectStorage();
|
||||
$this->requests = array();
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
@ -147,19 +151,7 @@ class ProfilerListener implements EventSubscriberInterface
|
||||
KernelEvents::REQUEST => array('onKernelRequest', 1024),
|
||||
KernelEvents::RESPONSE => array('onKernelResponse', -100),
|
||||
KernelEvents::EXCEPTION => 'onKernelException',
|
||||
KernelEvents::TERMINATE => array('onKernelTerminate', -1024),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the profile hierarchy.
|
||||
*
|
||||
* @param Profile $profile The root profile
|
||||
*/
|
||||
private function saveProfiles(Profile $profile)
|
||||
{
|
||||
$this->profiler->saveProfile($profile);
|
||||
foreach ($profile->getChildren() as $profile) {
|
||||
$this->saveProfiles($profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,8 +215,8 @@ class Profiler
|
||||
foreach ($this->collectors as $collector) {
|
||||
$collector->collect($request, $response, $exception);
|
||||
|
||||
// forces collectors to become "read/only" (they loose their object dependencies)
|
||||
$profile->addCollector(unserialize(serialize($collector)));
|
||||
// we need to clone for sub-requests
|
||||
$profile->addCollector(clone $collector);
|
||||
}
|
||||
|
||||
return $profile;
|
||||
|
Reference in New Issue
Block a user