Créer des extensions pour Tracy

Tracy est un excellent outil pour déboguer votre application. Vous voudrez cependant parfois avoir d'autres informations sous la main. Nous allons montrer comment écrire vos propres extensions pour la Tracy Bar afin de rendre le développement encore plus agréable.

  • Créer votre propre panneau de la Tracy Bar
  • Créer votre propre extension du BlueScreen

Vous trouverez un dépôt d'extensions toutes prêtes pour Tracy sur Componette.

Extensions de la Tracy Bar

Créer une nouvelle extension pour la Tracy Bar est simple. Créez un objet qui implémente l'interface Tracy\IBarPanel, laquelle comporte deux méthodes : getTab() et getPanel(). Ces méthodes doivent renvoyer le code HTML de l'onglet (une petite étiquette affichée directement sur la Bar) et du panneau (une fenêtre qui apparaît après un clic sur l'onglet). Si getPanel() ne renvoie rien, seul l'onglet lui-même est affiché. Si getTab() ne renvoie rien, rien n'est affiché du tout et getPanel() n'est pas appelée.

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

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

Enregistrement

L'enregistrement se fait en appelant Tracy\Debugger::getBar()->addPanel() :

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

Vous pouvez sinon enregistrer le panneau directement dans la configuration de l'application :

tracy:
	bar:
		- ExamplePanel

Code HTML de l'onglet

Il devrait ressembler à ceci :

<span title="Infobulle explicative">
	<svg>...</svg>
	<span class="tracy-label">Titre</span>
</span>

L'image devrait être au format SVG. Si une infobulle explicative n'est pas nécessaire, le <span> extérieur peut être omis.

Code HTML du panneau

Il devrait ressembler à ceci :

<h1>Titre</h1>

<div class="tracy-inner">
<div class="tracy-inner-container">
	... contenu ...
</div>
</div>

Le titre devrait être soit identique à celui de l'onglet, soit contenir des informations supplémentaires.

Gardez à l'esprit qu'une même extension peut être enregistrée plusieurs fois, éventuellement avec des réglages différents. Pour la mise en forme, vous ne pouvez donc pas utiliser d'identifiants CSS, seulement des classes, de préférence sous la forme tracy-addons-<NomDeClasse>[-<facultatif>]. Ajoutez cette classe au div à côté de la classe tracy-inner. Lors de l'écriture du CSS, il est utile de préfixer les sélecteurs par #tracy-debug .votre-classe, car cela donne à la règle une spécificité plus élevée que les styles de reset.

Styles par défaut

Dans le panneau, les éléments <a>, <table>, <pre> et <code> ont des styles prédéfinis. Si vous voulez créer un lien qui masque et affiche un autre élément, reliez-les par les attributs href et id et la classe tracy-toggle :

<a href="#tracy-addons-ClassName-{$counter}" class="tracy-toggle">Détails</a>

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

Si l'état par défaut est replié, ajoutez la classe tracy-collapsed aux deux éléments.

Utilisez un compteur statique pour éviter les identifiants en double sur une même page.

Assets personnalisés

Si votre panneau a besoin de sa propre feuille de style ou de son propre script, vous pouvez faire charger à Tracy des fichiers supplémentaires à côté des siens :

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

Prise en charge des agents IA

Lorsqu'un agent IA pilote le navigateur, Tracy envoie dans la console JS un résumé en markdown de la Tracy Bar. Les panneaux personnalisés peuvent fournir leur propre markdown en ajoutant une méthode getAgentInfo(): ?string à leur implémentation d'IBarPanel :

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";
	}
}

Le markdown renvoyé est inclus dans le résumé markdown de la barre. Quand la méthode manque ou renvoie null, le panneau est omis du résumé.

Voir l'intégration des agents IA à Tracy pour la vue d'ensemble.

Extensions du BlueScreen

Vous pouvez ainsi ajouter des visualisations d'exceptions ou des panneaux personnalisés qui apparaîtront sur l'écran bleu.

Une extension se crée ainsi :

Tracy\Debugger::getBlueScreen()->addPanel(function (?Throwable $e) { // exception attrapée
	return [
		'tab' => '...Titre...',
		'panel' => '...contenu HTML du panneau...',
	];
});

La fonction est appelée deux fois. D'abord, l'exception elle-même est passée dans le paramètre $e (si une exception est survenue) et le panneau renvoyé est rendu au début de la page. S'il renvoie null ou un tableau vide, le panneau n'est pas rendu. Elle est ensuite appelée avec $e = null et le panneau renvoyé est rendu sous la pile d'appels. Si la fonction renvoie 'bottom' => true dans le tableau, le panneau est rendu tout en bas.

Outre des panneaux, vous pouvez aussi ajouter des actions à l'aide d'addAction() : des liens ou des boutons cliquables qui apparaissent dans l'en-tête de la page d'erreur, à côté de ceux intégrés (comme search) :

Tracy\Debugger::getBlueScreen()->addAction(function (Throwable $e): ?array {
	if ($e instanceof MyException) {
		return [
			'link' => 'https://example.com/help?code=' . $e->getCode(),
			'label' => 'voir l\'aide',
		];
	}
	return null;
});

Le callback reçoit l'exception attrapée et renvoie un tableau avec les clés link et label, ou null s'il ne veut pas ajouter d'action pour l'exception donnée.

Action create file

Lorsque vous cliquez, sur la page d'erreur, sur un fichier qui n'existe pas encore, Tracy propose de le créer (l'action create file). Vous pouvez déterminer le contenu initial d'un tel fichier en enregistrant un générateur :

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;
});

Le callback reçoit le chemin du fichier cible et, quand il est connu, le nom de la classe qui doit y être définie. Il renvoie le contenu initial (le jeton $END$ marque l'endroit où le curseur sera placé et est retiré de la sortie), ou null pour laisser la décision à un autre générateur. Les générateurs sont essayés en commençant par le dernier enregistré ; le générateur intégré produit un simple squelette PHP.

Fibers et générateurs

Si une exception est levée pendant qu'une fiber ou un générateur est suspendu, sa pile ne fait pas partie de la pile d'appels ordinaire. Tracy affiche automatiquement la pile des fibers et générateurs accessibles depuis l'exception, mais l'une d'elles tournant indépendamment échapperait à cette détection. Vous pouvez l'ajouter manuellement au BlueScreen :

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