Создание расширений Tracy

Tracy – отличный инструмент для отладки вашего приложения. Однако иногда вам может понадобиться, чтобы под рукой была и другая информация. Мы покажем, как написать собственные расширения для Tracy Bar, чтобы разработка стала ещё приятнее.

  • Создание собственной панели Tracy Bar
  • Создание собственного расширения для красного экрана

Хранилище готовых расширений для Tracy вы найдёте на Componette.

Расширения Tracy Bar

Создать новое расширение для Tracy Bar просто. Создайте объект, реализующий интерфейс Tracy\IBarPanel, у которого есть два метода: getTab() и getPanel(). Эти методы должны вернуть HTML-код вкладки (небольшой ярлык, отображаемый прямо на панели Bar) и панели (всплывающее окно, отображаемое после щелчка по вкладке). Если getPanel() ничего не вернёт, отобразится только сама вкладка. Если ничего не вернёт getTab(), не отобразится ничего вообще, и getPanel() вызван не будет.

class ExamplePanel implements Tracy\IBarPanel
{
	public function getTab()
	{
		return /* ... */;
	}

	public function getPanel()
	{
		return /* ... */;
	}
}

Регистрация

Регистрация выполняется вызовом Tracy\Debugger::getBar()->addPanel():

Tracy\Debugger::getBar()->addPanel(new ExamplePanel);

Как вариант, панель можно зарегистрировать прямо в конфигурации приложения:

tracy:
	bar:
		- ExamplePanel

HTML-код вкладки

Должен выглядеть примерно так:

<span title="Поясняющая подсказка">
	<svg>...</svg>
	<span class="tracy-label">Заголовок</span>
</span>

Изображение должно быть в формате SVG. Если поясняющая подсказка не нужна, внешний <span> можно опустить.

HTML-код панели

Должен выглядеть примерно так:

<h1>Заголовок</h1>

<div class="tracy-inner">
<div class="tracy-inner-container">
	... содержимое ...
</div>
</div>

Заголовок должен либо совпадать с заголовком вкладки, либо содержать дополнительные сведения.

Помните, что одно расширение может быть зарегистрировано несколько раз, возможно с разными настройками. Поэтому для оформления нельзя использовать CSS-идентификаторы, только классы, желательно в формате tracy-addons-<ИмяКласса>[-<необязательное>]. Добавьте этот класс к div вместе с классом tracy-inner. При написании CSS полезно предварять селекторы #tracy-debug .ваш-класс, потому что так правило получает более высокую специфичность, чем сбрасывающие стили.

Стили по умолчанию

В панели у элементов <a>, <table>, <pre> и <code> есть заранее заданные стили. Если вы хотите сделать ссылку, скрывающую и показывающую другой элемент, свяжите их атрибутами href и id и классом tracy-toggle:

<a href="#tracy-addons-ClassName-{$counter}" class="tracy-toggle">Подробности</a>

<div id="tracy-addons-ClassName-{$counter}">...</div>

Если состояние по умолчанию – свёрнутое, добавьте обоим элементам класс tracy-collapsed.

Используйте статический счётчик, чтобы на одной странице не появились одинаковые идентификаторы.

Собственные ресурсы

Если вашей панели нужны собственные стили или скрипт, вы можете попросить Tracy загрузить дополнительные файлы вместе с её собственными ресурсами:

Tracy\Debugger::$customCssFiles[] = __DIR__ . '/panel.css';
Tracy\Debugger::$customJsFiles[] = __DIR__ . '/panel.js';

Поддержка AI-агентов

Когда браузером управляет AI-агент, Tracy отправляет в JS-консоль markdown-сводку панели Tracy Bar. Собственные панели могут предоставить свой markdown, добавив в свою реализацию IBarPanel метод getAgentInfo(): ?string:

class DatabasePanel implements Tracy\IBarPanel
{
	public function getTab(): string { /* ... */ }
	public function getPanel(): string { /* ... */ }

	public function getAgentInfo(): ?string
	{
		return "## Database\n\n- Queries: {$this->count}\n- Total time: {$this->time} ms\n";
	}
}

Возвращённый markdown включается в markdown-сводку панели. Если метода нет или он возвращает null, панель в сводку не попадает.

Полную картину смотрите в разделе Интеграция Tracy с AI-агентами.

Расширения красного экрана

Таким образом вы можете добавить собственные способы отображения исключений или панели, которые появятся на красном экране.

Расширение создаётся так:

Tracy\Debugger::getBlueScreen()->addPanel(function (?Throwable $e) { // перехваченное исключение
	return [
		'tab' => '...Заголовок...',
		'panel' => '...HTML-содержимое панели...',
	];
});

Функция вызывается дважды. Сначала в параметре $e передаётся само исключение (если оно произошло), и возвращённая панель отрисовывается в начале страницы. Если она вернёт null или пустой массив, панель не отрисовывается. Затем функция вызывается с $e = null, и возвращённая панель отрисовывается под стеком вызовов. Если функция вернёт в массиве 'bottom' => true, панель отрисуется в самом низу.

Кроме панелей, через addAction() можно добавить и действия – кликабельные ссылки или кнопки, которые появятся в шапке страницы с ошибкой рядом со встроенными (например, search):

Tracy\Debugger::getBlueScreen()->addAction(function (Throwable $e): ?array {
	if ($e instanceof MyException) {
		return [
			'link' => 'https://example.com/help?code=' . $e->getCode(),
			'label' => 'посмотреть справку',
		];
	}
	return null;
});

Callback получает перехваченное исключение и возвращает массив с ключами link и label либо null, если для данного исключения не хочет добавлять действие.

Действие create file

Когда вы на странице с ошибкой щёлкнете по файлу, которого ещё нет, Tracy предложит его создать (действие create file). Начальное содержимое такого файла можно задать, зарегистрировав генератор:

Tracy\Debugger::getBlueScreen()->addFileGenerator(function (string $file, ?string $class): ?string {
	if (str_ends_with($file, 'Test.php')) {
		return "<?php\n\nclass $class extends Tester\\TestCase\n{\n\t\$END\$\n}\n";
	}
	return null;
});

Callback получает путь к целевому файлу и, если оно известно, имя класса, который в нём должен быть определён. Он возвращает начальное содержимое (маркер $END$ обозначает место, куда встанет курсор, и из вывода удаляется) либо null, чтобы оставить решение другому генератору. Генераторы пробуются начиная с зарегистрированного последним; встроенный генератор порождает простой каркас PHP.

Файберы и генераторы

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

Tracy\Debugger::getBlueScreen()->addFiber($fiber);