From b8df582507ff04424032ede54221af8b84afed2c Mon Sep 17 00:00:00 2001 From: jelte Date: Wed, 19 Aug 2015 13:15:28 +0200 Subject: [PATCH] Extract the profiler to a new component --- components/index.rst | 1 + components/map.rst.inc | 4 + components/profiler/index.rst | 7 ++ components/profiler/introduction.rst | 73 ++++++++++++++++++ cookbook/profiler/data_collector.rst | 109 ++++++++++++++++++++------- cookbook/profiler/profiling_data.rst | 26 +++---- cookbook/profiler/storage.rst | 2 +- cookbook/testing/profiling.rst | 6 +- 8 files changed, 179 insertions(+), 49 deletions(-) create mode 100644 components/profiler/index.rst create mode 100644 components/profiler/introduction.rst diff --git a/components/index.rst b/components/index.rst index 41f72d049a4..0cf921206fa 100644 --- a/components/index.rst +++ b/components/index.rst @@ -23,6 +23,7 @@ The Components intl options_resolver process + profiler/index property_access/index routing/index security/index diff --git a/components/map.rst.inc b/components/map.rst.inc index 009284614ce..882989ab963 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -115,6 +115,10 @@ * :doc:`/components/process` +* :doc:`/components/profiler/index` + + * :doc:`/components/profiler/introduction` + * :doc:`/components/property_access/index` * :doc:`/components/property_access/introduction` diff --git a/components/profiler/index.rst b/components/profiler/index.rst new file mode 100644 index 00000000000..a7d43e7fe61 --- /dev/null +++ b/components/profiler/index.rst @@ -0,0 +1,7 @@ +Profiler +======== + +.. toctree:: + :maxdepth: 2 + + introduction diff --git a/components/profiler/introduction.rst b/components/profiler/introduction.rst new file mode 100644 index 00000000000..a2616444ecd --- /dev/null +++ b/components/profiler/introduction.rst @@ -0,0 +1,73 @@ +.. index:: + single: Profiler + single: Components; Profiler + +The Profiler Component +====================== + + The Profiler component provides tools to collected and present a profile of executed code. + +.. versionadded:: 2.8 + The Profiler component was introduced in Symfony 2.8. Previously, the classes + were located in the HttpKernel component. + +Installation +------------ + +You can install the component in many different ways: + +* :doc:`Install it via Composer ` (``symfony/profiler`` on `Packagist`_); +* Use the official Git repository (https://github.com/symfony/Profiler). + +Usage +----- + +The Profiler component provides several tools to help you debug PHP code. +Enabling them all is as easy as it can get:: + + use Symfony\Component\Profiler\Profiler; + use Symfony\Component\Profiler\Storage\FileProfilerStorage; + + $storage = new FileProfilerStorage(__DIR__.'/cache/profiler'); + $profiler = new Profiler($storage); + + // $profile is an implementation of ProfileInterface. + $profile = $profiler->profile(); + + $profiler->save( + $profile, + array( + 'url' => http://localhost/', + 'ip' => '127.0.0.1', + 'method' => 'GET', + 'response_code' => 200, + 'profile_type' => 'http', + ) + ); + +if your project makes use of the :doc:`EventDispatcher component `, you can automate the profiling by using the corresponding +EventListeners :class:`Symfony\\Component\\Profiler\\EventListener\\HttpProfilerListener`. + +.. caution:: + + You should limit the profiler tools in a production environment to only profile on Exceptions as + profile every request will generate a significant portion of data and increase the response time. + +Collecting Data with DataCollectors +----------------------------------- + +The Profiler assembles a Profile with data it gets from DataCollectors. + +A good deal of Components already provide usefull DataCollectors: + +* :doc:`Debug component `: :class:`Symfony\\Component\\Debug\\Profiler\\ExceptionDataCollector` +* :doc:`EventDispatcher component `: :class:`Symfony\\Component\\EventDispatcher\\Profiler\\EventDataCollector` +* :doc:`Form component `: :class:`Symfony\\Component\\Form\\Extension\\Profiler\\FormDataCollector` +* :doc:`HttpKernel component `: :class:`Symfony\\Component\\HttpKernel\\Profiler\\RequestDataCollector` & :class:`Symfony\\Component\\HttpKernel\\Profiler\\RouteDataCollector` +* :doc:`Security component `: :class:`Symfony\\Component\\Security\\Core\\Profiler\\SecurityDataCollector` +* :doc:`Translation component `: :class:`Symfony\\Component\\Translation\\Profiler\\TranslationDataCollector` +* :doc:`VarDumper component `: :class:`Symfony\\Component\\VarDumper\\Profiler\\DumpDataCollector` +* `Monolog bridge`: :class:`Symfony\\Bridge\\Monolog\\Profiler\\LoggerDataCollector` +* `Twig bridge`: :class:`Symfony\\Bridge\\Twig\\Profiler\\TwigDataCollector` + +.. _Packagist: https://packagist.org/packages/symfony/profiler diff --git a/cookbook/profiler/data_collector.rst b/cookbook/profiler/data_collector.rst index 985738aa5cb..cd8499ca277 100644 --- a/cookbook/profiler/data_collector.rst +++ b/cookbook/profiler/data_collector.rst @@ -4,7 +4,7 @@ How to Create a custom Data Collector ===================================== -:doc:`The Symfony Profiler ` delegates data collecting to +:doc:`The Symfony Profiler ` delegates data collecting to data collectors. Symfony comes bundled with a few of them, but you can easily create your own. @@ -12,65 +12,118 @@ Creating a custom Data Collector -------------------------------- Creating a custom data collector is as simple as implementing the -:class:`Symfony\\Component\\HttpKernel\\DataCollector\\DataCollectorInterface`:: +:class:`Symfony\\Component\\Profiler\\DataCollector\\DataCollectorInterface`:: interface DataCollectorInterface { /** - * Collects data for the given Request and Response. + * Returns the collected data. * - * @param Request $request A Request instance - * @param Response $response A Response instance - * @param \Exception $exception An Exception instance - */ - function collect(Request $request, Response $response, \Exception $exception = null); - - /** - * Returns the name of the collector. + * @return ProfileDataInterface * - * @return string The collector name + * @todo introduce in 3.0 */ - function getName(); + public function getCollectedData(); + } + +if the data should be collected just prior to the Profile being saved add the :class:`Symfony\\Component\\Profiler\\DataCollector\\LateDataCollectorInterface`:: + + interface LateDataCollectorInterface + { + } + +The ``getCollectedData()`` method is responsible for storing the data it wants to give +access to in a :class:`Symfony\\Component\\Profiler\\ProfileData\\ProfileDataInterface`:: + + interface ProfileDataInterface extends \Serializable + { + public function getName(); } The ``getName()`` method must return a unique name. This is used to access the information later on (see :doc:`/cookbook/testing/profiling` for instance). -The ``collect()`` method is responsible for storing the data it wants to give -access to in local properties. - .. caution:: - As the profiler serializes data collector instances, you should not + As the profiler serializes ProfileData instances, you should not store objects that cannot be serialized (like PDO objects), or you need to provide your own ``serialize()`` method. -Most of the time, it is convenient to extend -:class:`Symfony\\Component\\HttpKernel\\DataCollector\\DataCollector` and -populate the ``$this->data`` property (it takes care of serializing the -``$this->data`` property):: +Example DataCollector:: - class MemoryDataCollector extends DataCollector + class MemoryDataCollector extends AbstractDataCollector implements LateDataCollectorInterface { - public function collect(Request $request, Response $response, \Exception $exception = null) + private $memoryLimit; + + /** + * Constructor. + */ + public function __construct() { - $this->data = array( - 'memory' => memory_get_peak_usage(true), - ); + $this->memoryLimit = ini_get('memory_limit'); } - public function getMemory() + /** + * {@inheritdoc} + */ + public function lateCollect() + { + return new MemoryData(memory_get_peak_usage(true), $this->memoryLimit); + } + } + + class MemoryData implements ProfileDataInterface + { + private $memory; + private $memoryLimit; + + /** + * Constructor. + * + * @param int $memory The current used memory. + * @param int $memoryLimit The memory limit. + */ + public function __construct($memory, $memoryLimit) { - return $this->data['memory']; + $this->memory = $memory; + $this->memoryLimit = $this->convertToBytes($memoryLimit); } + /** + * {@inheritdoc} + */ public function getName() { return 'memory'; } + + /** + * Returns the memory. + * + * @return int The memory + */ + public function getMemory() + { + return $this->memory; + } + + /** + * Returns the PHP memory limit. + * + * @return int The memory limit + */ + public function getMemoryLimit() + { + return $this->memoryLimit; + } + + //... } + + + .. _data_collector_tag: Enabling custom Data Collectors diff --git a/cookbook/profiler/profiling_data.rst b/cookbook/profiler/profiling_data.rst index 761dae943e6..320a828eab1 100644 --- a/cookbook/profiler/profiling_data.rst +++ b/cookbook/profiler/profiling_data.rst @@ -6,22 +6,15 @@ How to Access Profiling Data Programmatically Most of the times, the profiler information is accessed and analyzed using its web-based visualizer. However, you can also retrieve profiling information -programmatically thanks to the methods provided by the ``profiler`` service. - -When the response object is available, use the -:method:`Symfony\\Component\\HttpKernel\\Profiler\\Profiler::loadProfileFromResponse` -method to access to its associated profile:: - - // ... $profiler is the 'profiler' service - $profile = $profiler->loadProfileFromResponse($response); +programmatically thanks to the methods provided by the ``profiler.storage`` service. When the profiler stores data about a request, it also associates a token with it; this token is available in the ``X-Debug-Token`` HTTP header of the response. Using this token, you can access the profile of any past response thanks to the -:method:`Symfony\\Component\\HttpKernel\\Profiler\\Profiler::loadProfile` method:: +:method:`Symfony\\Component\\Profiler\\Storage\\ProfilerStorageInterface::read` method:: $token = $response->headers->get('X-Debug-Token'); - $profile = $container->get('profiler')->loadProfile($token); + $profile = $container->get('profiler.storage')->read($token); .. tip:: @@ -29,22 +22,21 @@ Using this token, you can access the profile of any past response thanks to the with your browser's developer tools to get the value of the ``X-Debug-Token`` HTTP header. -The ``profiler`` service also provides the -:method:`Symfony\\Component\\HttpKernel\\Profiler\\Profiler::find` method to +The ``profiler.storage`` service also provides the +:method:`Symfony\\Component\\Profiler\\Storage\\ProfilerStorageInterface::findBy` method to look for tokens based on some criteria:: // get the latest 10 tokens - $tokens = $container->get('profiler')->find('', '', 10, '', ''); + $tokens = $container->get('profiler.storage')->findBy(array(), 10, '', ''); // get the latest 10 tokens for all URL containing /admin/ - $tokens = $container->get('profiler')->find('', '/admin/', 10, '', ''); + $tokens = $container->get('profiler.storage')->findBy(array('url' => '/admin/'), 10, '', ''); // get the latest 10 tokens for local requests - $tokens = $container->get('profiler')->find('127.0.0.1', '', 10, '', ''); + $tokens = $container->get('profiler.storage')->findBy(array('ip' => '127.0.0.1'), 10, '', ''); // get the latest 10 tokens for requests that happened between 2 and 4 days ago - $tokens = $container->get('profiler') - ->find('', '', 10, '4 days ago', '2 days ago'); + $tokens = $container->get('profiler.storage')->findBy(array(), 10, '4 days ago', '2 days ago'); Lastly, if you want to manipulate profiling data on a different machine than the one where the information was generated, use the ``profiler:export`` and diff --git a/cookbook/profiler/storage.rst b/cookbook/profiler/storage.rst index e91ef883eaa..f31efa79f4c 100644 --- a/cookbook/profiler/storage.rst +++ b/cookbook/profiler/storage.rst @@ -57,7 +57,7 @@ uses MySQL as the storage for the profiler with a lifetime of one hour: ), )); -The :doc:`HttpKernel component ` currently +The :doc:`Profiler component ` currently supports the following profiler storage drivers: * file diff --git a/cookbook/testing/profiling.rst b/cookbook/testing/profiling.rst index 1789c69dbc9..f74d5d24be5 100644 --- a/cookbook/testing/profiling.rst +++ b/cookbook/testing/profiling.rst @@ -34,13 +34,13 @@ the ``test`` environment):: // check the number of requests $this->assertLessThan( 10, - $profile->getCollector('db')->getQueryCount() + $profile->get('db')->getQueryCount() ); // check the time spent in the framework $this->assertLessThan( 500, - $profile->getCollector('time')->getDuration() + $profile->get('time')->getDuration() ); } } @@ -52,7 +52,7 @@ finish. It's easy to achieve if you embed the token in the error message:: $this->assertLessThan( 30, - $profile->getCollector('db')->getQueryCount(), + $profile->get('db')->getQueryCount(), sprintf( 'Checks that query count is less than 30 (token %s)', $profile->getToken()