Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • SensioLabs Professional services to help you with Symfony
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Profiler
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • Installation
  • Accessing Profiling Data Programmatically
  • Data Collectors
  • Timing the Execution of the Application
  • Enabling the Profiler Programmatically
  • Updating the Web Debug Toolbar After AJAX Requests
  • Creating a Data Collector
    • Adding Web Profiler Templates
    • Enabling Custom Data Collectors

Profiler

Edit this page

Profiler

The profiler is a powerful development tool that gives detailed information about the execution of any request.

Caution

Never enable the profiler in production environments as it will lead to major security vulnerabilities in your project.

Installation

In applications using Symfony Flex, run this command to install the profiler Symfony pack before using it:

1
$ composer require --dev symfony/profiler-pack

Now, browse any page of your application in the development environment to let the profiler collect information. Then, click on any element of the debug toolbar injected at the bottom of your pages to open the web interface of the Symfony Profiler, which will look like this:

Note

The debug toolbar is only injected into HTML responses. For other kinds of contents (e.g. JSON responses in API requests) the profiler URL is available in the X-Debug-Token-Link HTTP response header. Browse the /_profiler URL to see all profiles.

6.3

Profile garbage collection was introduced in Symfony 6.3.

Note

To limit the storage used by profiles on disk, they are probabilistically removed after 2 days.

Accessing Profiling Data Programmatically

Most of the time, the profiler information is accessed and analyzed using its web-based interface. 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 loadProfileFromResponse() method to access to its associated profile:

1
2
// ... $profiler is the 'profiler' service
$profile = $profiler->loadProfileFromResponse($response);

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 loadProfile() method:

1
2
$token = $response->headers->get('X-Debug-Token');
$profile = $profiler->loadProfile($token);

Tip

When the profiler is enabled but not the web debug toolbar, inspect the page with your browser's developer tools to get the value of the X-Debug-Token HTTP header.

The profiler service also provides the find() method to look for tokens based on some criteria:

1
2
3
4
5
6
7
8
9
10
11
// gets the latest 10 tokens
$tokens = $profiler->find('', '', 10, '', '', '');

// gets the latest 10 tokens for all URL containing /admin/
$tokens = $profiler->find('', '/admin/', 10, '', '', '');

// gets the latest 10 tokens for local POST requests
$tokens = $profiler->find('127.0.0.1', '', 10, 'POST', '', '');

// gets the latest 10 tokens for requests that happened between 2 and 4 days ago
$tokens = $profiler->find('', '', 10, '', '4 days ago', '2 days ago');

Data Collectors

The profiler gets its information using some services called "data collectors". Symfony comes with several collectors that get information about the request, the logger, the routing, the cache, etc.

Run this command to get the list of collectors actually enabled in your app:

1
$ php bin/console debug:container --tag=data_collector

You can also create your own data collector to store any data generated by your app and display it in the debug toolbar and the profiler web interface.

Timing the Execution of the Application

If you want to measure the time some tasks take in your application, there's no need to create a custom data collector. Instead, use the built-in utilities to profile Symfony applications.

Tip

Consider using a professional profiler such as Blackfire to measure and analyze the execution of your application in detail.

Enabling the Profiler Programmatically

Symfony Profiler can be enabled and disabled programmatically. You can use the enable() and disable() methods of the Profiler class in your controllers to manage the profiler programmatically:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\HttpKernel\Profiler\Profiler;
// ...

class DefaultController
{
    // ...

    public function someMethod(?Profiler $profiler)
    {
        // $profiler won't be set if your environment doesn't have the profiler (like prod, by default)
        if (null !== $profiler) {
            // if it exists, disable the profiler for this particular controller action
            $profiler->disable();
        }

        // ...
    }
}

In order for the profiler to be injected into your controller you need to create an alias pointing to the existing profiler service:

1
2
3
# config/services_dev.yaml
services:
    Symfony\Component\HttpKernel\Profiler\Profiler: '@profiler'
1
2
3
4
5
6
7
8
9
10
11
<!-- config/services_dev.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="Symfony\Component\HttpKernel\Profiler\Profiler" alias="profiler"/>
    </services>
</container>
1
2
3
4
// config/services_dev.php
use Symfony\Component\HttpKernel\Profiler\Profiler;

$container->setAlias(Profiler::class, 'profiler');

Updating the Web Debug Toolbar After AJAX Requests

Single-page applications (SPA) are web applications that interact with the user by dynamically rewriting the current page rather than loading entire new pages from a server.

By default, the debug toolbar displays the information of the initial page load and doesn't refresh after each AJAX request. However, you can set the Symfony-Debug-Toolbar-Replace header to a value of 1 in the response to the AJAX request to force the refresh of the toolbar:

1
$response->headers->set('Symfony-Debug-Toolbar-Replace', 1);

Ideally this header should only be set during development and not for production. To do that, create an event subscriber and listen to the kernel.response event:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelInterface;

// ...

class MySubscriber implements EventSubscriberInterface
{
    public function __construct(
        private KernelInterface $kernel,
    ) {
    }

    // ...

    public function onKernelResponse(ResponseEvent $event)
    {
        if (!$this->kernel->isDebug()) {
            return;
        }

        $request = $event->getRequest();
        if (!$request->isXmlHttpRequest()) {
            return;
        }

        $response = $event->getResponse();
        $response->headers->set('Symfony-Debug-Toolbar-Replace', 1);
    }
}

Creating a Data Collector

The Symfony Profiler obtains its profiling and debug information using some special classes called data collectors. Symfony comes bundled with a few of them, but you can also create your own.

A data collector is a PHP class that implements the DataCollectorInterface. For convenience, your data collectors can also extend from the AbstractDataCollector class, which implements the interface and provides some utilities and the $this->data property to store the collected information.

The following example shows a custom collector that stores information about the request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/DataCollector/RequestCollector.php
namespace App\DataCollector;

use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class RequestCollector extends AbstractDataCollector
{
    public function collect(Request $request, Response $response, \Throwable $exception = null)
    {
        $this->data = [
            'method' => $request->getMethod(),
            'acceptable_content_types' => $request->getAcceptableContentTypes(),
        ];
    }
}

These are the method that you can define in the data collector class:

collect() method:

Stores the collected data in local properties ($this->data if you extend from AbstractDataCollector). If you need some services to collect the data, inject those services in the data collector constructor.

Caution

The collect() method is only called once. It is not used to "gather" data but is there to "pick up" the data that has been stored by your service.

Caution

As the profiler serializes data collector instances, you should not store objects that cannot be serialized (like PDO objects) or you need to provide your own serialize() method.

reset() method:
It's called between requests to reset the state of the profiler. By default it only empties the $this->data contents, but you can override this method to do additional cleaning.
getName() method:
Returns the collector identifier, which must be unique in the application. By default it returns the FQCN of the data collector class, but you can override this method to return a custom name (e.g. app.request_collector). This value is used later to access the collector information (see How to Use the Profiler in a Functional Test) so you may prefer using short strings instead of FQCN strings.

The collect() method is called during the kernel.response event. If you need to collect data that is only available later, implement LateDataCollectorInterface and define the lateCollect() method, which is invoked right before the profiler data serialization (during kernel.terminate event).

Note

If you're using the default services.yaml configuration with autoconfigure, then Symfony will start using your data collector after the next page refresh. Otherwise, enable the data collector by hand.

Adding Web Profiler Templates

The information collected by your data collector can be displayed both in the web debug toolbar and in the web profiler. To do so, you need to create a Twig template that includes some specific blocks.

First, add the getTemplate() method in your data collector class to return the path of the Twig template to use. Then, add some getters to give the template access to the collected information:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/DataCollector/RequestCollector.php
namespace App\DataCollector;

use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector;

class RequestCollector extends AbstractDataCollector
{
    // ...

    public static function getTemplate(): ?string
    {
        return 'data_collector/template.html.twig';
    }

    public function getMethod()
    {
        return $this->data['method'];
    }

    public function getAcceptableContentTypes()
    {
        return $this->data['acceptable_content_types'];
    }
}

In the simplest case, you want to display the information in the toolbar without providing a profiler panel. This requires to define the toolbar block and set the value of two variables called icon and text:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{# templates/data_collector/template.html.twig #}
{% extends '@WebProfiler/Profiler/layout.html.twig' %}

{% block toolbar %}
    {% set icon %}
        {# this is the content displayed as a panel in the toolbar #}
        <svg xmlns="http://www.w3.org/2000/svg"> ... </svg>
        <span class="sf-toolbar-value">Request</span>
    {% endset %}

    {% set text %}
        {# this is the content displayed when hovering the mouse over
           the toolbar panel #}
        <div class="sf-toolbar-info-piece">
            <b>Method</b>
            <span>{{ collector.method }}</span>
        </div>

        <div class="sf-toolbar-info-piece">
            <b>Accepted content type</b>
            <span>{{ collector.acceptableContentTypes|join(', ') }}</span>
        </div>
    {% endset %}

    {# the 'link' value set to 'false' means that this panel doesn't
       show a section in the web profiler #}
    {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: false }) }}
{% endblock %}

Tip

Symfony Profiler icons are selected from Tabler icons, a large and open source collection of SVG icons. It's recommended to also use those icons for your own profiler panels to get a consistent look.

Tip

Built-in collector templates define all their images as embedded SVG files. This makes them work everywhere without having to mess with web assets links:

1
2
3
4
{% set icon %}
    {{ include('data_collector/icon.svg') }}
    {# ... #}
{% endset %}

If the toolbar panel includes extended web profiler information, the Twig template must also define additional blocks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{# templates/data_collector/template.html.twig #}
{% extends '@WebProfiler/Profiler/layout.html.twig' %}

{% block toolbar %}
    {% set icon %}
        {# ... #}
    {% endset %}

    {% set text %}
        <div class="sf-toolbar-info-piece">
            {# ... #}
        </div>
    {% endset %}

    {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': true }) }}
{% endblock %}

{% block head %}
    {# Optional. Here you can link to or define your own CSS and JS contents. #}
    {# Use {{ parent() }} to extend the default styles instead of overriding them. #}
{% endblock %}

{% block menu %}
    {# This left-hand menu appears when using the full-screen profiler. #}
    <span class="label">
        <span class="icon"><img src="..." alt=""/></span>
        <strong>Request</strong>
    </span>
{% endblock %}

{% block panel %}
    {# Optional, for showing the most details. #}
    <h2>Acceptable Content Types</h2>
    <table>
        <tr>
            <th>Content Type</th>
        </tr>

        {% for type in collector.acceptableContentTypes %}
        <tr>
            <td>{{ type }}</td>
        </tr>
        {% endfor %}
    </table>
{% endblock %}

The menu and panel blocks are the only required blocks to define the contents displayed in the web profiler panel associated with this data collector. All blocks have access to the collector object.

Note

The position of each panel in the toolbar is determined by the collector priority, which can only be defined when configuring the data collector by hand.

Note

If you're using the default services.yaml configuration with autoconfigure, then Symfony will start displaying your collector data in the toolbar after the next page refresh. Otherwise, enable the data collector by hand.

Enabling Custom Data Collectors

If you don't use Symfony's default configuration with autowire and autoconfigure you'll need to configure the data collector explicitly:

1
2
3
4
5
6
7
8
9
10
11
12
# config/services.yaml
services:
    App\DataCollector\RequestCollector:
        tags:
            -
                name: data_collector
                # must match the value returned by the getName() method
                id: 'App\DataCollector\RequestCollector'
                # optional template (it has more priority than the value returned by getTemplate())
                template: 'data_collector/template.html.twig'
                # optional priority (positive or negative integer; default = 0)
                # priority: 300
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="App\DataCollector\RequestCollector">
            <!-- the 'template' attribute has more priority than the value returned by getTemplate() -->
            <tag name="data_collector"
                id="App\DataCollector\RequestCollector"
                template="data_collector/template.html.twig"
            />
            <!-- optional 'priority' attribute (positive or negative integer; default = 0) -->
            <!-- priority="300" -->
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use App\DataCollector\RequestCollector;

return function(ContainerConfigurator $containerConfigurator) {
    $services = $containerConfigurator->services();

    $services->set(RequestCollector::class)
        ->tag('data_collector', [
            'id' => RequestCollector::class,
            // optional template (it has more priority than the value returned by getTemplate())
            'template' => 'data_collector/template.html.twig',
            // optional priority (positive or negative integer; default = 0)
            // 'priority' => 300,
        ]);
};
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:

    Symfony 6.2 is backed by

    Symfony 6.2 is backed by

    Online Sylius certification, take it now!

    Online Sylius certification, take it now!

    Peruse our complete Symfony & PHP solutions catalog for your web development needs.

    Peruse our complete Symfony & PHP solutions catalog for your web development needs.

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Andras Debreczeni, a Symfony contributor

    Thanks Andras Debreczeni for being a Symfony contributor

    1 commit • 2 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony

    Search by Algolia