Profiler

Дата обновления перевода 2024-08-01

Profiler

Профилировщик - это мощный инструмент разработки, который предоставляет подробную информацию о выполнении любого запроса.

Caution

Никогда не включайте профилировщик в окружениях производства, так как это приведет к серьезным уязвимостям в безопасности вашего проекта.

Установка

В приложениях, использующих Symfony Flex , выполните эту команду, чтобы установить пакет Symfony profiler перед его использованием:

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

Теперь просмотрите любую страницу вашего приложения в окружении разработки, чтобы позволить профилировщику собрать информацию. Затем щелкните на любом элементе панели инструментов отладки, расположенной в нижней части ваших страниц, чтобы открыть веб-интерфейс Symfony Profiler, который будет выглядеть следующим образом:

Страница веб-профилировщика Symfony.

Note

Панель инструментов отладки внедряется только в HTML-ответы. Для других типов содержания (например, ответов JSON в запросах API) URL профилировщика доступен в заголовке HTTP-ответа X-Debug-Token-Link. Перейдите по URL /_profiler, чтобы увидеть все профили.

Note

Для ограничения объема памяти, используемой профилями на диске, они с высокой вероятностью удаляются через 2 дня.

Программный доступ к данным профилирования

В большинстве случаев, доступ к информации профилировщика и ее анализ осуществляется с помощью его веб-интерфейса. Однако вы также можете получить информацию о профилировании программным путем благодаря методам, предоставляемым сервисом ``profiler''.

Если объект ответа доступен, используйте loadProfileFromResponse() для доступа к ассоциированному с ним профилю:

// ... $profiler - это сервис 'profiler' $profile = $profiler->loadProfileFromResponse($response);

Когда профилировщик сохраняет данные о запросе, он также ассоциирует с ними токен; этот токен доступен в HTTP-заголовке ответа X-Debug-Token. Используя этот токен, вы можете получить доступ к профилю любого прошлого ответа, благодаря методу loadProfile():

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

Tip

Если профилировшик включен, а панель инструментов веб-отладки - нет, исследуйте страницу с помощью инструментов разработчика вашего браузера, чтобы получить значение HTTP-заголовка X-Debug-Token.

Сервис profiler также предоставляет метод find() для поиска токенов на основе некоторых критериев:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// получает последние 10 токенов
$tokens = $profiler->find('', '', 10, '', '', '');

// получает последние 10 токенов для всех URL, содержащих /admin/
$tokens = $profiler->find('', '/admin/', 10, '', '', '');

// получает последние 10 токенов для всех URL, не содержащих /api/
$tokens = $profiler->find('', '!/api/', 10, '', '', '');

// получает последние 10 токенов для локальных запросов POST
$tokens = $profiler->find('127.0.0.1', '', 10, 'POST', '', '');

// получает последние 10 токенов для запросов, которые произошли от 2 до 4 дней назад
$tokens = $profiler->find('', '', 10, '', '4 days ago', '2 days ago');

Сборщики данных

Профилировщик получает информацию с помощью некоторых сервисов, называемых "сборщиками данных". Symfony поставляется с несколькими сборщиками, которые получают информацию о запросе, логгере, маршрутизации, кеше и т.д.

Выполните эту команду, чтобы получить список сборщиков, действительно включенных в вашем приложении:

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

Вы также можете создать свой собственный сборщик данных , чтобы хранить любые данные, генерируемые вашим приложением, и отображать их на панели инструментов отладки и в веб-интерфейсе профилировщика.

Определение времени выполнения приложения

Если вы хотите измерить время выполнения некоторых задач в вашем приложении, нет необходимости создавать собственный сборщик данных. Вместо этого используйте встроенные утилиты для профилирования приложений Symfony .

Tip

Рассмотрите возможность использования профессионального профилировщика, такого как Blackfire, для измерения и детального анализа выполнения вашего приложения.

Программное включение профилировщика

Symfony Profiler можно включать и отключать программно. Вы можете использовать методы enable() и disable() класса Profiler в ваших контроллерах, чтобы управлять профилировщиком программно:

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): Response
    {
        // $profiler не будет установлен, если ваше окружение не имеет профилировшик (как prod, по умолчанию)
        if (null !== $profiler) {
            // если он существует, отключите профилировщик для этого конкретного действия контроллера
            $profiler->disable();
        }

        // ...
    }
}

Для того чтобы профилировщик был внедрен в ваш контроллер, вам необходимо создать псевдоним, указывающий на существующий сервис profiler:

1
2
3
# config/services_dev.yaml
services:
    Symfony\Component\HttpKernel\Profiler\Profiler: '@profiler'

Включение профилировщика с условиями

Вместо того чтобы включать профилировщик программно, как объяснялось в предыдущем разделе, можно также включить его при выполнении определенного условия (например, если определенный параметр включен в URL):

1
2
3
4
5
# config/packages/dev/web_profiler.yaml
    framework:
        profiler:
            collect: false
            collect_parameter: 'profile'

Эта конфигурация отключает профилировщик по умолчанию (collect: false), для повышения производительности приложения; но включает его для запросов, содержащих параметр запроса с именем profile (вы можете свободно выбирать имя этого параметра запроса).

Помимо параметра запроса, эта функция также работает при отправке поля формы с таким именем (полезно для включения профилировщика в POST запросах) или при включении его в качестве атрибута запроса.

Обновление панели инструментов веб-отладки после запросов AJAX

Одностраничные приложения (SPA) - это веб-приложения, которые взаимодействуют с пользователем, динамически переписывая текущую страницу, а не загружая целые новые страницы с сервера.

По умолчанию, панель инструментов отладки отображает информацию о начальной загрузке страницы и не обновляется после каждого запроса AJAX. Однако вы можете установить заголовок Symfony-Debug-Toolbar-Replace в значение 1 в ответе на AJAX-запрос, чтобы принудительно обновить панель инструментов:

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

В идеале этот заголовок должен быть установлен только во время разработки, а не для производства. Для этого создайте подписчика событий и слушайте событие kernel.response :

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): void
    {
        if (!$this->kernel->isDebug()) {
            return;
        }

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

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

Создание сборщика данных

Symfony Profiler получает информацию о профилировании и отладке с помощью некоторых специальных классов, называемых сборщиками данных. Symfony поставляется в комплекте с несколькими из них, но вы также можете создать свой собственный.

Сборщик данных - это класс PHP, который реализует DataCollectorInterface. Для удобства, ваши сборщики данных могут также расширяться из класса AbstractDataCollector, который реализует интерфейс и предоставляет некоторые утилиты, а также свойство $this->data для хранения собранной информации.

В следующем примере показан пользовательский сборщик, который хранит информацию о запросе:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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): void
    {
        $this->data = [
            'method' => $request->getMethod(),
            'acceptable_content_types' => $request->getAcceptableContentTypes(),
        ];
    }
}

Вот методы, которые вы можете определить в классе сборщика данных:
Метод collect():

Сохраняет собранные данные в локальных свойствах ($this->data, если вы расширяете из AbstractDataCollector). Если вам нужны некоторые сервисы для сбора данных, внедрите эти сервисы в конструктор сборщика данных.

Caution

Метод collect() вызывается только один раз. Он не используется для "сбора" данных, но используется для "подбирания" данных, которые были сохранены вашим сервисом.

Caution

Поскольку профилировщик сериализует экземпляры коллектора данных, вам не следует хранить объекты, которые не могут быть сериализованы (например, объекты PDO) или вам необходимо предоставить свой собственный метод serialize().

Метод reset():
Вызывается между запросами, чтобы сбросить состояние профилировщика. По умолчанию он только очищает содержание $this->data, но вы можете переопределить этот метод для дополнительной очистки.
Метод getName():
Возвращает идентификатор сборщика, который должен быть уникальным в приложении. По умолчанию возвращается FQCN класса сборщика данных, но вы можете переопределить этот метод, чтобы вернуть пользовательское имя (например, app.request_collector). Это значение используется позже для доступа к информации о сборщике (см. Как использовать профилировщик в функциональном тесте), поэтому вы можете предпочесть использование коротких строк вместо строк FQCN.

Метод collect() вызывается во время события kernel.response . Если вам нужно собрать данные, которые будут доступны только позже, реализуйте LateDataCollectorInterface и определите метод lateCollect(), который вызывается непосредственно перед сериализацией данных профилировщика (во время события kernel.terminate ).

Note

Если вы используете конфигурацию services.yaml по умолчанию с autoconfigure, то Symfony начнет использовать ваш сборщик данных после следующего обновления страницы. В противном случае, включите коллектор данных вручную.

Добавление шаблонов веб-профилировщика

Информация, собранная вашим сборщиком данных, может быть отображена как в панели инструментов веб-отладки, так и в веб-профилировщике. Для этого вам необходимо создать шаблон Twig, который включает в себя несколько определенных блоков.

Во-первых, добавьте метод getTemplate() в ваш класс сборщика данных, чтобы вернуть путь к используемому шаблону Twig. Затем добавьте несколько геттеров, чтобы дать шаблону доступ к собранной информации:

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
// src/DataCollector/RequestCollector.php
namespace App\DataCollector;

use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector;
use Symfony\Component\VarDumper\Cloner\Data;

class RequestCollector extends AbstractDataCollector
{
    // ...

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

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

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

    public function getSomeObject(): Data
    {
        // использовать метод cloneVar() для сброса собранных данных в профилировщике
        return $this->cloneVar($this->data['method']);
    }
}

В простейшем случае вы хотите отобразить информацию на панели инструментов без предоставления панели профилировщика. Для этого необходимо определить блок toolbar и установить значение двух переменных - icon и 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 %}
        {# это содержание, отображённое как панель в панели инструментов #}
        <svg xmlns="http://www.w3.org/2000/svg"> ... </svg>
        <span class="sf-toolbar-value">Request</span>
    {% endset %}

    {% set text %}
        {# это содержание, отображённое при наведении мышью на
           на панель инструментов #}
        <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 %}

    {# значение 'link' установленное как 'false', означает, что эта панель не
       отображает раздел в веб-профилировщике #}
    {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: false }) }}
{% endblock %}

Tip

Иконки Symfony Profiler выбраны из иконок Tabler, большой и открытой коллекция SVG иконок с открытым исходным кодом. Рекомендуется также использовать эти иконки для ваших собственных панелей профилировщика, чтобы добиться единообразия внешнего вида.

Tip

Встроенные шаблоны сборщиков определяют все свои изображения как встроенные SVG-файлы. Это позволяет им работать везде без необходимости возиться со ссылками на веб-ресурсы:

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

Если панель инструментов включает расширенную информацию веб-профилировщика, шаблон Twig должен также определить дополнительные блоки:

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 %}
    {# Опционально. Здесь вы можете сослаться на или определить ваше собственное содержание CSS и JS. #}
    {# Используйте {{ parent() }}, чтобы расширить стили по умолчанию, вместо того, чтобы переопределять их. #}
{% endblock %}

{% block menu %}
    {# Это левостороннее меню возникает при использовании полноэкранного профилировщика. #}
    <span class="label">
        <span class="icon"><img src="..." alt=""/></span>
        <strong>Request</strong>
    </span>
{% endblock %}

{% block panel %}
    {# Опционально, для отображения наибольшего количества деталей. #}
    <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 %}

Блоки menu и panel являются единственными обязательными блоками для определения содержимого, отображаемого на панели веб-профилировщика, ассоциированного с этим сборщиком данных. Все блоки имеют доступ к объекту collector.

Note

Положение каждой панели на панели инструментов определяется приоритетом сборщика, который может быть определен только при конфигурировании сборщика данных вручную.

Note

Если вы используете конфигурацию services.yaml по умолчанию с autoconfigure, то Symfony начнет отображать данные вашего сборщика в панели инструментов после следующего обновления страницы. В противном случае, включите сборщик данных вручную.

Включение пользовательских сборщиков данных

Если вы не используете конфигурацию Symfony по умолчанию с автомонтированием и автоконфигурацией , вам нужно будет явно сконфигурировать сборщик данных:

1
2
3
4
5
6
7
8
9
10
11
12
# config/services.yaml
services:
    App\DataCollector\RequestCollector:
        tags:
            -
                name: data_collector
                # должно совпадать со значением, возвращённым методом getName()
                id: 'App\DataCollector\RequestCollector'
                # опциональный шаблон (имеет больший приоритет, чем значение, возвращённое getTemplate())
                template: 'data_collector/template.html.twig'
                # опциональный приоритет (положительное или отрицательное целое число; по умолчанию = 0)
                # priority: 300