Recettes
Content Security Policy
Si votre site utilise une Content Security Policy (CSP), vous devrez ajouter 'nonce-<valeur>' et
'strict-dynamic' à la directive script-src pour que Tracy fonctionne correctement. Certains plugins
tiers peuvent exiger des directives supplémentaires. Le nonce n'est pas pris en charge dans la directive style-src ;
si vous utilisez cette directive, vous devez ajouter 'unsafe-inline', mais cela devrait être évité en mode
production.
Exemple de configuration pour Nette Framework :
http:
csp:
script-src: [nonce, strict-dynamic]
Exemple en PHP pur :
$nonce = base64_encode(random_bytes(20));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic';");
Chargement plus rapide
L'intégration de base est simple. Si votre page web comporte cependant des scripts bloquants qui se chargent lentement, ils
peuvent ralentir le chargement de Tracy. La solution est de placer <?php Tracy\Debugger::renderLoader() ?> dans
votre template avant tout script :
<!DOCTYPE html>
<html>
<head>
<title>...<title>
<?php Tracy\Debugger::renderLoader() ?>
<link rel="stylesheet" href="assets/style.css">
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
</head>
Localiser la source d'une sortie
Avez-vous déjà rencontré Cannot modify header information – headers already sent ? Cela apparaît quand quelque chose (une espace égarée, une ligne vide ou un BOM au début d'un fichier) est envoyé au navigateur avant que votre code ne pose un en-tête HTTP ou ne démarre une session. Trouver le coupable est fastidieux.
Tracy\OutputDebugger vous aide. Activez-le tout au début de votre programme :
Tracy\OutputDebugger::enable();
Il surveille toutes les sorties et, à la fin de la page, affiche la liste de tous les endroits d'où une sortie a été envoyée, avec le fichier, la ligne et un lien qui l'ouvre dans votre éditeur. Un byte order mark (BOM) au début d'un fichier est mis en évidence lui aussi, car c'est une cause invisible fréquente du problème.
Déboguer les requêtes AJAX
Tracy capture automatiquement les requêtes AJAX émises par jQuery ou par l'API native fetch. Ces requêtes
s'affichent comme des lignes supplémentaires dans la Tracy Bar, ce qui rend le débogage AJAX simple et commode.
Si vous ne voulez pas capturer automatiquement les requêtes AJAX, vous pouvez désactiver cette fonctionnalité en définissant la variable JavaScript :
window.TracyAutoRefresh = false;
Pour surveiller manuellement certaines requêtes AJAX, ajoutez l'en-tête HTTP X-Tracy-Ajax avec la valeur
renvoyée par Tracy.getAjaxHeader(). Voici un exemple d'utilisation avec la fonction fetch :
fetch(url, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-Tracy-Ajax': Tracy.getAjaxHeader(),
}
})
Cette approche permet de déboguer les requêtes AJAX de façon sélective.
Stockage des données
Tracy sait afficher les panneaux de la Tracy Bar et les écrans bleus pour les requêtes AJAX et les redirections. Tracy crée
ses propres sessions, stocke les données dans ses propres fichiers temporaires et utilise un cookie
tracy-session.
Tracy peut aussi être configurée pour utiliser une session native de PHP, qui doit être démarrée avant l'activation de Tracy :
session_start();
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
Si le démarrage d'une session exige une initialisation plus complexe, vous pouvez démarrer Tracy immédiatement (afin qu'elle
puisse traiter les erreurs qui surviennent), puis initialiser le gestionnaire de session. Informez enfin Tracy que la session est
prête à l'emploi à l'aide de la fonction dispatch() :
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
// suivi de l'initialisation de la session
// et du démarrage de la session
session_start();
Debugger::dispatch();
La fonction setSessionStorage() existe depuis la version 2.9 ; avant cela, Tracy utilisait toujours la session
native de PHP.
Scrubber personnalisé
Un Scrubber est un filtre qui empêche les données sensibles de fuir depuis les dumps, comme les mots de passe ou les
identifiants. Le filtre est appelé pour chaque élément du tableau ou de l'objet affiché et renvoie true si la
valeur est sensible. Dans ce cas, ***** est affiché à la place de la valeur.
// empêche l'affichage des valeurs des clés et propriétés comme `password`,
// `password_repeat`, `check_password`, `DATABASE_PASSWORD`, etc.
$scrubber = function(string $key, $value, ?string $class): bool
{
return preg_match('#password#i', $key) && $value !== null;
};
// l'utilise pour tous les dumps du BlueScreen
Tracy\Debugger::getBlueScreen()->scrubber = $scrubber;
Logger personnalisé
Nous pouvons créer un logger personnalisé qui journalisera les erreurs et les exceptions non attrapées, et qui sera aussi
invoqué par la méthode Tracy\Debugger::log(). Le logger doit implémenter l'interface Tracy\ILogger.
use Tracy\ILogger;
class SlackLogger implements ILogger
{
public function log($value, $priority = ILogger::INFO)
{
// envoie une requête à Slack
}
}
Puis nous l'activons :
Tracy\Debugger::setLogger(new SlackLogger);
Si vous utilisez tout Nette Framework, vous pouvez le définir dans le fichier de configuration NEON :
services:
tracy.logger: SlackLogger
Intégration de Monolog
Le paquet Tracy fournit un adaptateur PSR-3, qui permet d'intégrer monolog/monolog.
$monolog = new Monolog\Logger('main-channel');
$monolog->pushHandler(new Monolog\Handler\StreamHandler($logFilePath, Monolog\Logger::DEBUG));
$tracyLogger = new Tracy\Bridges\Psr\PsrToTracyLoggerAdapter($monolog);
Debugger::setLogger($tracyLogger);
Debugger::enable();
Debugger::log('info'); // écrit : [<TIMESTAMP>] main-channel.INFO: info [] []
Debugger::log('warning', Debugger::WARNING); // écrit : [<TIMESTAMP>] main-channel.WARNING: warning [] []
Intégration de Sentry
Vous pouvez transmettre les erreurs à un service comme Sentry tout en conservant la journalisation propre à Tracy. L'idée est d'envelopper le logger d'origine : le nouveau logger transmet le message à Sentry, puis délègue au précédent, si bien que les journaux fichier et les notifications par e-mail continuent de fonctionner.
use Sentry\Severity;
use Tracy\Debugger;
use Tracy\ILogger;
class SentryLogger implements ILogger
{
private ILogger $originalLogger;
public function __construct(string $dsn)
{
$this->originalLogger = Debugger::getLogger();
\Sentry\init(['dsn' => $dsn]);
}
public function log(mixed $value, string $level = self::INFO)
{
// envoie à Sentry
if ($severity = $this->getSeverity($level)) {
$value instanceof \Throwable
? \Sentry\captureException($value)
: \Sentry\captureMessage((string) $value, $severity);
}
// conserve la journalisation d'origine de Tracy (fichiers, e-mail)
return $this->originalLogger->log($value, $level);
}
private function getSeverity(string $level): ?Severity
{
return match ($level) {
ILogger::DEBUG => Severity::debug(),
ILogger::INFO => Severity::info(),
ILogger::WARNING => Severity::warning(),
ILogger::ERROR, ILogger::EXCEPTION => Severity::error(),
ILogger::CRITICAL => Severity::fatal(),
default => null,
};
}
}
Activez-le comme n'importe quel autre logger personnalisé :
Debugger::setLogger(new SentryLogger('https://public@sentry.example.com/1'));
Dans une application Nette, enregistrez-le plutôt comme service tracy.logger :
services:
tracy.logger: SentryLogger('https://public@sentry.example.com/1')
nginx
Si Tracy ne fonctionne pas sous nginx, celui-ci est probablement mal configuré. S'il y a quelque chose comme :
try_files $uri $uri/ /index.php;
changez-le en :
try_files $uri $uri/ /index.php$is_args$args;