Nette Documentation Preview

syntax
Poradniki
*********


Content Security Policy
=======================

Jeśli Twoja strona używa Content Security Policy (CSP), musisz dodać do dyrektywy `script-src` `'nonce-<wartość>'` i `'strict-dynamic'`, żeby Tracy działała poprawnie. Niektóre pluginy zewnętrzne mogą wymagać dodatkowych dyrektyw. Nonce nie jest wspierany w dyrektywie `style-src`; jeśli tej dyrektywy używasz, musisz dodać `'unsafe-inline'`, ale w trybie produkcyjnym należy tego unikać.

Przykład konfiguracji dla [Nette Framework |nette:configuring]:

```neon
http:
	csp:
		script-src: [nonce, strict-dynamic]
```

Przykład w czystym PHP:

```php
$nonce = base64_encode(random_bytes(20));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic';");
```


Szybsze wczytywanie
===================

Podstawowa integracja jest prosta. Jeśli jednak masz na swojej stronie wolno wczytujące się blokujące skrypty, mogą one spowolnić wczytywanie Tracy. Rozwiązaniem jest umieszczenie w szablonie `<?php Tracy\Debugger::renderLoader() ?>` przed jakimikolwiek skryptami:

```latte
<!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>
```


Znajdowanie źródła wyjścia
==========================

Natrafiłeś kiedyś na *Cannot modify header information - headers already sent*? Pojawia się, gdy coś (zabłąkana spacja, pusta linia albo BOM na początku pliku) zostanie wysłane do przeglądarki, zanim Twój kod ustawi nagłówek HTTP albo uruchomi sesję. Znalezienie winowajcy jest żmudne.

Pomaga `Tracy\OutputDebugger`. Włącz go jako pierwszą rzecz w swoim programie:

```php
Tracy\OutputDebugger::enable();
```

Śledzi całe wyjście i na końcu strony wypisuje listę każdego miejsca, z którego wyjście zostało wysłane, wraz z plikiem, linią i odnośnikiem otwierającym je w Twoim edytorze. Byte order mark (BOM) na początku pliku również jest podświetlany, bo jest częstą niewidoczną przyczyną problemu.


Debugowanie żądań AJAX
======================

Tracy automatycznie przechwytuje żądania AJAX wykonywane przez jQuery albo natywne API `fetch`. Żądania te wyświetlane są jako dodatkowe wiersze w Tracy Barze, co umożliwia łatwe i wygodne debugowanie AJAX-a.

Jeśli nie chcesz przechwytywać żądań AJAX automatycznie, możesz tę funkcję wyłączyć, ustawiając zmienną JavaScriptową:

```js
window.TracyAutoRefresh = false;
```

Do ręcznego monitorowania konkretnych żądań AJAX dodaj nagłówek HTTP `X-Tracy-Ajax` z wartością zwracaną przez `Tracy.getAjaxHeader()`. Oto przykład użycia z funkcją `fetch`:

```js
fetch(url, {
    headers: {
        'X-Requested-With': 'XMLHttpRequest',
        'X-Tracy-Ajax': Tracy.getAjaxHeader(),
    }
})
```

To podejście pozwala na selektywne debugowanie żądań AJAX.


Przechowywanie danych
=====================

Tracy potrafi wyświetlać panele Tracy Bara i Bluescreeny dla żądań AJAX i przekierowań. Tracy tworzy własne sesje, przechowuje dane we własnych plikach tymczasowych i używa cookie `tracy-session`.

Tracy można też skonfigurować tak, żeby używała natywnej sesji PHP, którą trzeba uruchomić przed włączeniem Tracy:

```php
session_start();
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
```

Jeśli uruchomienie sesji wymaga bardziej złożonej inicjalizacji, możesz uruchomić Tracy natychmiast (żeby mogła obsłużyć ewentualne błędy), a potem zainicjalizować handler sesji. Na koniec poinformuj Tracy, że sesja jest gotowa do użycia, funkcją `dispatch()`:

```php
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();

// następnie inicjalizacja sesji
// i uruchomienie sesji
session_start();

Debugger::dispatch();
```

Funkcja `setSessionStorage()` istnieje od wersji 2.9; wcześniej Tracy zawsze używała natywnej sesji PHP.


Własny scrubber
===============

Scrubber to filtr zapobiegający wyciekowi wrażliwych danych z dumpów, na przykład haseł albo danych uwierzytelniających. Filtr wywoływany jest dla każdej pozycji dumpowanej tablicy albo obiektu i zwraca `true`, jeśli wartość jest wrażliwa. W takim przypadku zamiast wartości wypisywane jest `*****`.

```php
// zapobiega dumpowaniu wartości kluczy i właściwości takich jak `password`,
// `password_repeat`, `check_password`, `DATABASE_PASSWORD` itd.
$scrubber = function(string $key, $value, ?string $class): bool
{
	return preg_match('#password#i', $key) && $value !== null;
};

// używamy go dla wszystkich dumpów wewnątrz BlueScreenu
Tracy\Debugger::getBlueScreen()->scrubber = $scrubber;
```


Własny logger
=============

Możemy utworzyć własny logger, który będzie logował błędy, nieprzechwycone wyjątki, a także będzie wywoływany metodą `Tracy\Debugger::log()`. Logger musi implementować interfejs [api:Tracy\ILogger].

```php
use Tracy\ILogger;

class SlackLogger implements ILogger
{
	public function log($value, $priority = ILogger::INFO)
	{
		// wysyła żądanie do Slacka
	}
}
```

A następnie go aktywujemy:

```php
Tracy\Debugger::setLogger(new SlackLogger);
```

Jeśli używasz całego Nette Framework, możesz ustawić go w pliku konfiguracyjnym NEON:

```neon
services:
	tracy.logger: SlackLogger
```


Integracja z Monologiem
-----------------------

Pakiet Tracy dostarcza adapter PSR-3, umożliwiając integrację z [monolog/monolog](https://github.com/Seldaek/monolog).

```php
$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'); // zapisze: [<TIMESTAMP>] main-channel.INFO: info [] []
Debugger::log('warning', Debugger::WARNING); // zapisze: [<TIMESTAMP>] main-channel.WARNING: warning [] []
```


Integracja z Sentry
-------------------

Możesz przekazywać błędy do usługi takiej jak [Sentry |https://sentry.io], zachowując przy tym własne logowanie Tracy. Pomysł polega na opakowaniu pierwotnego loggera: nowy logger przekazuje komunikat do Sentry, a potem deleguje do poprzedniego, więc logi plikowe i powiadomienia e-mailem działają dalej.

```php
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)
	{
		// wysyłamy do Sentry
		if ($severity = $this->getSeverity($level)) {
			$value instanceof \Throwable
				? \Sentry\captureException($value)
				: \Sentry\captureMessage((string) $value, $severity);
		}

		// zachowujemy pierwotne logowanie Tracy (pliki, 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,
		};
	}
}
```

Aktywujesz go tak samo jak każdy inny własny logger:

```php
Debugger::setLogger(new SentryLogger('https://public@sentry.example.com/1'));
```

W aplikacji Nette zarejestruj go zamiast tego jako usługę `tracy.logger`:

```neon
services:
	tracy.logger: SentryLogger('https://public@sentry.example.com/1')
```


nginx
=====

Jeśli Tracy nie działa na nginksie, prawdopodobnie jest źle skonfigurowany. Jeśli jest tam coś w rodzaju:

```nginx
try_files $uri $uri/ /index.php;
```

zmień to na:

```nginx
try_files $uri $uri/ /index.php$is_args$args;
```

Poradniki

Content Security Policy

Jeśli Twoja strona używa Content Security Policy (CSP), musisz dodać do dyrektywy script-src 'nonce-<wartość>' i 'strict-dynamic', żeby Tracy działała poprawnie. Niektóre pluginy zewnętrzne mogą wymagać dodatkowych dyrektyw. Nonce nie jest wspierany w dyrektywie style-src; jeśli tej dyrektywy używasz, musisz dodać 'unsafe-inline', ale w trybie produkcyjnym należy tego unikać.

Przykład konfiguracji dla Nette Framework:

http:
	csp:
		script-src: [nonce, strict-dynamic]

Przykład w czystym PHP:

$nonce = base64_encode(random_bytes(20));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic';");

Szybsze wczytywanie

Podstawowa integracja jest prosta. Jeśli jednak masz na swojej stronie wolno wczytujące się blokujące skrypty, mogą one spowolnić wczytywanie Tracy. Rozwiązaniem jest umieszczenie w szablonie <?php Tracy\Debugger::renderLoader() ?> przed jakimikolwiek skryptami:

<!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>

Znajdowanie źródła wyjścia

Natrafiłeś kiedyś na Cannot modify header information – headers already sent? Pojawia się, gdy coś (zabłąkana spacja, pusta linia albo BOM na początku pliku) zostanie wysłane do przeglądarki, zanim Twój kod ustawi nagłówek HTTP albo uruchomi sesję. Znalezienie winowajcy jest żmudne.

Pomaga Tracy\OutputDebugger. Włącz go jako pierwszą rzecz w swoim programie:

Tracy\OutputDebugger::enable();

Śledzi całe wyjście i na końcu strony wypisuje listę każdego miejsca, z którego wyjście zostało wysłane, wraz z plikiem, linią i odnośnikiem otwierającym je w Twoim edytorze. Byte order mark (BOM) na początku pliku również jest podświetlany, bo jest częstą niewidoczną przyczyną problemu.

Debugowanie żądań AJAX

Tracy automatycznie przechwytuje żądania AJAX wykonywane przez jQuery albo natywne API fetch. Żądania te wyświetlane są jako dodatkowe wiersze w Tracy Barze, co umożliwia łatwe i wygodne debugowanie AJAX-a.

Jeśli nie chcesz przechwytywać żądań AJAX automatycznie, możesz tę funkcję wyłączyć, ustawiając zmienną JavaScriptową:

window.TracyAutoRefresh = false;

Do ręcznego monitorowania konkretnych żądań AJAX dodaj nagłówek HTTP X-Tracy-Ajax z wartością zwracaną przez Tracy.getAjaxHeader(). Oto przykład użycia z funkcją fetch:

fetch(url, {
    headers: {
        'X-Requested-With': 'XMLHttpRequest',
        'X-Tracy-Ajax': Tracy.getAjaxHeader(),
    }
})

To podejście pozwala na selektywne debugowanie żądań AJAX.

Przechowywanie danych

Tracy potrafi wyświetlać panele Tracy Bara i Bluescreeny dla żądań AJAX i przekierowań. Tracy tworzy własne sesje, przechowuje dane we własnych plikach tymczasowych i używa cookie tracy-session.

Tracy można też skonfigurować tak, żeby używała natywnej sesji PHP, którą trzeba uruchomić przed włączeniem Tracy:

session_start();
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();

Jeśli uruchomienie sesji wymaga bardziej złożonej inicjalizacji, możesz uruchomić Tracy natychmiast (żeby mogła obsłużyć ewentualne błędy), a potem zainicjalizować handler sesji. Na koniec poinformuj Tracy, że sesja jest gotowa do użycia, funkcją dispatch():

Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();

// następnie inicjalizacja sesji
// i uruchomienie sesji
session_start();

Debugger::dispatch();

Funkcja setSessionStorage() istnieje od wersji 2.9; wcześniej Tracy zawsze używała natywnej sesji PHP.

Własny scrubber

Scrubber to filtr zapobiegający wyciekowi wrażliwych danych z dumpów, na przykład haseł albo danych uwierzytelniających. Filtr wywoływany jest dla każdej pozycji dumpowanej tablicy albo obiektu i zwraca true, jeśli wartość jest wrażliwa. W takim przypadku zamiast wartości wypisywane jest *****.

// zapobiega dumpowaniu wartości kluczy i właściwości takich jak `password`,
// `password_repeat`, `check_password`, `DATABASE_PASSWORD` itd.
$scrubber = function(string $key, $value, ?string $class): bool
{
	return preg_match('#password#i', $key) && $value !== null;
};

// używamy go dla wszystkich dumpów wewnątrz BlueScreenu
Tracy\Debugger::getBlueScreen()->scrubber = $scrubber;

Własny logger

Możemy utworzyć własny logger, który będzie logował błędy, nieprzechwycone wyjątki, a także będzie wywoływany metodą Tracy\Debugger::log(). Logger musi implementować interfejs Tracy\ILogger.

use Tracy\ILogger;

class SlackLogger implements ILogger
{
	public function log($value, $priority = ILogger::INFO)
	{
		// wysyła żądanie do Slacka
	}
}

A następnie go aktywujemy:

Tracy\Debugger::setLogger(new SlackLogger);

Jeśli używasz całego Nette Framework, możesz ustawić go w pliku konfiguracyjnym NEON:

services:
	tracy.logger: SlackLogger

Integracja z Monologiem

Pakiet Tracy dostarcza adapter PSR-3, umożliwiając integrację z 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'); // zapisze: [<TIMESTAMP>] main-channel.INFO: info [] []
Debugger::log('warning', Debugger::WARNING); // zapisze: [<TIMESTAMP>] main-channel.WARNING: warning [] []

Integracja z Sentry

Możesz przekazywać błędy do usługi takiej jak Sentry, zachowując przy tym własne logowanie Tracy. Pomysł polega na opakowaniu pierwotnego loggera: nowy logger przekazuje komunikat do Sentry, a potem deleguje do poprzedniego, więc logi plikowe i powiadomienia e-mailem działają dalej.

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)
	{
		// wysyłamy do Sentry
		if ($severity = $this->getSeverity($level)) {
			$value instanceof \Throwable
				? \Sentry\captureException($value)
				: \Sentry\captureMessage((string) $value, $severity);
		}

		// zachowujemy pierwotne logowanie Tracy (pliki, 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,
		};
	}
}

Aktywujesz go tak samo jak każdy inny własny logger:

Debugger::setLogger(new SentryLogger('https://public@sentry.example.com/1'));

W aplikacji Nette zarejestruj go zamiast tego jako usługę tracy.logger:

services:
	tracy.logger: SentryLogger('https://public@sentry.example.com/1')

nginx

Jeśli Tracy nie działa na nginksie, prawdopodobnie jest źle skonfigurowany. Jeśli jest tam coś w rodzaju:

try_files $uri $uri/ /index.php;

zmień to na:

try_files $uri $uri/ /index.php$is_args$args;