Anleitungen
Content Security Policy
Wenn Ihre Site eine Content Security Policy (CSP) verwendet, müssen Sie der Direktive script-src
'nonce-<value>' und 'strict-dynamic' hinzufügen, damit Tracy richtig funktioniert. Manche Plugins
von Drittanbietern können weitere Direktiven erfordern. In der Direktive style-src wird nonce nicht unterstützt;
wenn Sie diese Direktive verwenden, müssen Sie 'unsafe-inline' ergänzen, was im Produktionsmodus jedoch vermieden
werden sollte.
Beispielkonfiguration für das Nette Framework:
http:
csp:
script-src: [nonce, strict-dynamic]
Beispiel in reinem PHP:
$nonce = base64_encode(random_bytes(20));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic';");
Schnelleres Laden
Die grundlegende Einbindung ist unkompliziert. Wenn Sie auf Ihrer Webseite jedoch langsam ladende, blockierende Skripte haben,
können diese das Laden von Tracy verlangsamen. Die Lösung ist, <?php Tracy\Debugger::renderLoader() ?> in
Ihrem Template vor alle Skripte zu setzen:
<!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>
Die Quelle der Ausgabe finden
Sind Sie schon einmal auf Cannot modify header information – headers already sent gestoßen? Das taucht auf, wenn etwas (ein versehentliches Leerzeichen, eine Leerzeile oder ein BOM am Anfang einer Datei) an den Browser gesendet wird, bevor Ihr Code einen HTTP-Header setzt oder eine Session startet. Den Verursacher zu finden, ist mühsam.
Tracy\OutputDebugger hilft. Aktivieren Sie ihn als Allererstes in Ihrem Programm:
Tracy\OutputDebugger::enable();
Er überwacht die gesamte Ausgabe und gibt am Ende der Seite eine Liste aller Stellen aus, von denen Ausgaben gesendet wurden, samt Datei, Zeile und einem Link, der sie in Ihrem Editor öffnet. Ein Byte Order Mark (BOM) am Anfang einer Datei wird ebenfalls hervorgehoben, denn es ist eine häufige unsichtbare Ursache des Problems.
Debugging von AJAX-Requests
Tracy erfasst AJAX-Requests, die mit jQuery oder der nativen fetch-API gestellt werden, automatisch. Diese
Requests werden als zusätzliche Zeilen in der Tracy Bar angezeigt und ermöglichen so ein einfaches und bequemes Debugging
von AJAX.
Wenn Sie AJAX-Requests nicht automatisch erfassen wollen, können Sie diese Funktion abschalten, indem Sie die JavaScript-Variable setzen:
window.TracyAutoRefresh = false;
Für die manuelle Überwachung bestimmter AJAX-Requests fügen Sie den HTTP-Header X-Tracy-Ajax mit dem Wert
hinzu, den Tracy.getAjaxHeader() zurückgibt. Hier ein Beispiel für die Verwendung mit der Funktion
fetch:
fetch(url, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-Tracy-Ajax': Tracy.getAjaxHeader(),
}
})
Dieser Ansatz erlaubt das gezielte Debuggen einzelner AJAX-Requests.
Datenspeicher
Tracy kann Panels der Tracy Bar und Bluescreens für AJAX-Requests und Weiterleitungen anzeigen. Tracy erzeugt dafür eigene
Sessions, speichert die Daten in eigenen temporären Dateien und verwendet ein Cookie tracy-session.
Tracy lässt sich auch so konfigurieren, dass es die native PHP-Session verwendet, die vor dem Aktivieren von Tracy gestartet werden muss:
session_start();
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
Falls der Start der Session eine komplexere Initialisierung erfordert, können Sie Tracy sofort starten (damit es eventuell
auftretende Fehler behandeln kann) und den Session-Handler danach initialisieren. Zum Schluss teilen Sie Tracy mit der Funktion
dispatch() mit, dass die Session bereit zur Verwendung ist:
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
// danach folgt die Initialisierung der Session
// und der Start der Session
session_start();
Debugger::dispatch();
Die Funktion setSessionStorage() gibt es seit Version 2.9; davor verwendete Tracy immer die native
PHP-Session.
Eigener Scrubber
Ein Scrubber ist ein Filter, der verhindert, dass sensible Daten aus Dumps nach außen dringen, etwa Passwörter oder
Zugangsdaten. Der Filter wird für jedes Element des ausgegebenen Arrays oder Objekts aufgerufen und gibt true
zurück, wenn der Wert sensibel ist. In diesem Fall wird statt des Werts ***** ausgegeben.
// verhindert die Ausgabe von Werten zu Schlüsseln und Properties wie `password`,
// `password_repeat`, `check_password`, `DATABASE_PASSWORD` usw.
$scrubber = function(string $key, $value, ?string $class): bool
{
return preg_match('#password#i', $key) && $value !== null;
};
// für alle Dumps innerhalb des BlueScreen verwenden
Tracy\Debugger::getBlueScreen()->scrubber = $scrubber;
Eigener Logger
Wir können einen eigenen Logger erstellen, der Fehler und nicht abgefangene Exceptions protokolliert und außerdem von der
Methode Tracy\Debugger::log() aufgerufen wird. Der Logger muss das Interface Tracy\ILogger implementieren.
use Tracy\ILogger;
class SlackLogger implements ILogger
{
public function log($value, $priority = ILogger::INFO)
{
// sendet einen Request an Slack
}
}
Und dann aktivieren wir ihn:
Tracy\Debugger::setLogger(new SlackLogger);
Wenn Sie das vollständige Nette Framework verwenden, können Sie ihn in der NEON-Konfigurationsdatei setzen:
services:
tracy.logger: SlackLogger
Monolog-Integration
Das Paket Tracy enthält einen PSR-3-Adapter, der die Einbindung von monolog/monolog erlaubt.
$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'); // schreibt: [<TIMESTAMP>] main-channel.INFO: info [] []
Debugger::log('warning', Debugger::WARNING); // schreibt: [<TIMESTAMP>] main-channel.WARNING: warning [] []
Sentry-Integration
Sie können Fehler an einen Dienst wie Sentry weiterleiten und dabei die eigene Protokollierung von Tracy behalten. Die Idee ist, den ursprünglichen Logger zu umhüllen: Der neue Logger gibt die Meldung an Sentry weiter und delegiert dann an den vorherigen, sodass Dateilogs und E-Mail-Benachrichtigungen weiter funktionieren.
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)
{
// an Sentry senden
if ($severity = $this->getSeverity($level)) {
$value instanceof \Throwable
? \Sentry\captureException($value)
: \Sentry\captureMessage((string) $value, $severity);
}
// die ursprüngliche Protokollierung von Tracy behalten (Dateien, 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,
};
}
}
Aktivieren Sie ihn wie jeden anderen eigenen Logger:
Debugger::setLogger(new SentryLogger('https://public@sentry.example.com/1'));
In einer Nette-Anwendung registrieren Sie ihn stattdessen als Service tracy.logger:
services:
tracy.logger: SentryLogger('https://public@sentry.example.com/1')
nginx
Wenn Tracy unter nginx nicht funktioniert, ist es vermutlich falsch konfiguriert. Wenn dort etwas steht wie:
try_files $uri $uri/ /index.php;
ändern Sie es zu:
try_files $uri $uri/ /index.php$is_args$args;