Guías
Content Security Policy
Si su sitio usa Content Security Policy (CSP), tendrá que añadir 'nonce-<valor>' y
'strict-dynamic' a la directiva script-src para que Tracy funcione correctamente. Algunos plugins de
terceros pueden requerir directivas adicionales. El nonce no está soportado en la directiva style-src; si usa esa
directiva, tendrá que añadir 'unsafe-inline', pero eso debería evitarse en modo de producción.
Ejemplo de configuración para Nette Framework:
http:
csp:
script-src: [nonce, strict-dynamic]
Ejemplo en PHP puro:
$nonce = base64_encode(random_bytes(20));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic';");
Carga más rápida
La integración básica es sencilla. Pero, si en su página web tiene scripts bloqueantes que cargan despacio, pueden
ralentizar la carga de Tracy. La solución es colocar <?php Tracy\Debugger::renderLoader() ?> en su plantilla
antes de cualquier 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>
Localizar el origen de la salida
¿Se ha topado alguna vez con Cannot modify header information – headers already sent? Aparece cuando algo (un espacio perdido, una línea en blanco o un BOM al principio de un archivo) se envía al navegador antes de que su código establezca una cabecera HTTP o inicie una sesión. Encontrar al culpable es tedioso.
Tracy\OutputDebugger ayuda. Actívelo como lo primerísimo de su programa:
Tracy\OutputDebugger::enable();
Vigila toda la salida y al final de la página imprime una lista de todos los lugares desde los que se envió salida, junto con el archivo, la línea y un enlace que lo abre en su editor. También resalta el byte order mark (BOM) al principio de un archivo, porque es una causa invisible habitual del problema.
Depurar peticiones AJAX
Tracy captura automáticamente las peticiones AJAX hechas con jQuery o con la API nativa fetch. Esas peticiones
se muestran como filas adicionales en la Tracy Bar, lo que permite depurar AJAX de forma fácil y cómoda.
Si no quiere capturar las peticiones AJAX automáticamente, puede desactivar esta función poniendo la variable de JavaScript:
window.TracyAutoRefresh = false;
Para vigilar manualmente peticiones AJAX concretas, añada la cabecera HTTP X-Tracy-Ajax con el valor que devuelve
Tracy.getAjaxHeader(). Aquí tiene un ejemplo de uso con la función fetch:
fetch(url, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-Tracy-Ajax': Tracy.getAjaxHeader(),
}
})
Este enfoque permite depurar peticiones AJAX de forma selectiva.
Almacenamiento de los datos
Tracy puede mostrar los paneles de la Tracy Bar y las Bluescreens de las peticiones AJAX y de las redirecciones. Tracy crea sus
propias sesiones, guarda los datos en sus propios archivos temporales y usa la cookie tracy-session.
También se puede configurar Tracy para que use la sesión nativa de PHP, que debe iniciarse antes de activar Tracy:
session_start();
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
Si iniciar la sesión requiere una inicialización más compleja, puede arrancar Tracy de inmediato (para que pueda ocuparse de
los errores que se produzcan) e inicializar después el manejador de la sesión. Por último, informe a Tracy de que la sesión ya
se puede usar con la función dispatch():
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
// a continuación, la inicialización de la sesión
// y el inicio de la sesión
session_start();
Debugger::dispatch();
La función setSessionStorage() existe desde la versión 2.9; antes de eso, Tracy usaba siempre la sesión nativa
de PHP.
Scrubber propio
Un Scrubber es un filtro que impide que los datos sensibles se filtren en los volcados, como las contraseñas o las
credenciales. El filtro se llama para cada elemento del array o del objeto volcado y devuelve true si el valor es
sensible. En ese caso se imprime ***** en lugar del valor.
// impide volcar los valores de claves y propiedades como `password`,
// `password_repeat`, `check_password`, `DATABASE_PASSWORD`, etc.
$scrubber = function(string $key, $value, ?string $class): bool
{
return preg_match('#password#i', $key) && $value !== null;
};
// lo usa para todos los volcados dentro de la BlueScreen
Tracy\Debugger::getBlueScreen()->scrubber = $scrubber;
Logger propio
Podemos crear un logger propio que registre los errores y las excepciones no capturadas y al que llame también el método
Tracy\Debugger::log(). El logger debe implementar la interfaz Tracy\ILogger.
use Tracy\ILogger;
class SlackLogger implements ILogger
{
public function log($value, $priority = ILogger::INFO)
{
// envía una petición a Slack
}
}
Y después lo activamos:
Tracy\Debugger::setLogger(new SlackLogger);
Si usa todo Nette Framework, puede configurarlo en el archivo de configuración NEON:
services:
tracy.logger: SlackLogger
Integración con Monolog
El paquete Tracy ofrece un adaptador PSR-3 que permite integrar 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'); // escribe: [<TIMESTAMP>] main-channel.INFO: info [] []
Debugger::log('warning', Debugger::WARNING); // escribe: [<TIMESTAMP>] main-channel.WARNING: warning [] []
Integración con Sentry
Puede reenviar los errores a un servicio como Sentry y conservar a la vez el registro propio de Tracy. La idea es envolver el logger original: el nuevo logger pasa el mensaje a Sentry y después delega en el anterior, con lo que el registro en archivos y las notificaciones por correo siguen funcionando.
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)
{
// envía a Sentry
if ($severity = $this->getSeverity($level)) {
$value instanceof \Throwable
? \Sentry\captureException($value)
: \Sentry\captureMessage((string) $value, $severity);
}
// conserva el registro original de Tracy (archivos, correo)
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,
};
}
}
Actívelo igual que cualquier otro logger propio:
Debugger::setLogger(new SentryLogger('https://public@sentry.example.com/1'));
En una aplicación Nette, regístrelo en su lugar como el servicio tracy.logger:
services:
tracy.logger: SentryLogger('https://public@sentry.example.com/1')
nginx
Si Tracy no funciona en nginx, probablemente esté mal configurado. Si hay algo como:
try_files $uri $uri/ /index.php;
cámbielo por:
try_files $uri $uri/ /index.php$is_args$args;