Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New StreamLogger & MultiLogger #556

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
060461b
removed PHP 7 stuff
dg Oct 12, 2022
b77a2f6
removed support for Nette __toString workaround
dg Dec 10, 2021
2b136ad
removed support for $context in error handler
dg Apr 22, 2022
ebe8fd4
removed FireLogger (BC break)
dg Dec 15, 2021
d83c812
updated tests
dg Dec 10, 2021
d149ed2
coding style
dg Mar 8, 2023
31ee43e
coding style: reformatted Assert::exception
dg Apr 7, 2022
10ad9b3
added property typehints
dg Feb 3, 2022
df64c19
added PHP 8 typehints
dg Dec 16, 2021
8fd56c0
used constructor promotion
dg Mar 2, 2023
ec5276c
Dumper: added ArrayIterator exposer
dg May 9, 2022
905c3d0
used native PHP 8 features
dg Dec 12, 2021
f4c3cbc
CSP: requires 'script-dynamic'
dg May 6, 2022
ae32c0f
Debugger::timer: switch from microtime to hrtime
bckp Jan 1, 2022
df85dcb
BlueScreen: added support for sensitive markers /*sensitive{*/ and /*…
dg Feb 27, 2023
960e86c
Describer::addPropertyTo() added $described
dg Mar 7, 2023
1363c82
Dumper: added support for enum/flags properties
dg Mar 7, 2023
d97a773
BlueScreen: simplified HTML comment escaping
dg Mar 8, 2023
c50307f
BlueScreen: added source to header
dg Mar 8, 2023
f875221
Released version 2.10.0
dg Feb 28, 2023
5788ef0
opened 3.0-dev
dg Dec 10, 2021
c358afd
uses PascalCase constants
dg Dec 5, 2022
4fd42f8
readme: added PSR3 adapter for monolog/monolog example
paxperscientiam Mar 20, 2022
133f8c9
removed bridge for Latte
dg Dec 20, 2021
22f4af8
Logger: added typehints
dg Feb 3, 2022
02641ee
shadow dom for dumper [WIP]
dg May 31, 2022
93c74fc
shadow dom for bar [WIP]
dg Jun 1, 2022
f2464fd
shadow dom for bluescreen [WIP]
dg Nov 18, 2022
1ac7a0a
Logger: add StreamLogger
f3l1x Mar 8, 2023
60d4fa8
Logger: add MultiLogger
f3l1x Mar 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}
],
"require": {
"php": ">=8.0 <8.3",
"php": ">=7.2 <8.3",
"ext-session": "*",
"ext-json": "*"
},
Expand All @@ -42,7 +42,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.10-dev"
"dev-master": "3.0-dev"
}
}
}
4 changes: 1 addition & 3 deletions examples/assets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ h2 {
font-size: 140%;
}

pre.tracy-dump {
tracy-dump {
border: 1px solid silver;
padding: 1em;
margin: 1em 0;
}

a {
Expand Down
45 changes: 0 additions & 45 deletions examples/firelogger.php

This file was deleted.

55 changes: 22 additions & 33 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ Alternatively, you can download the whole package or [tracy.phar](https://github

| Tracy | compatible with PHP | compatible with browsers
|-----------|---------------|----------
| Tracy 2.10| PHP 8.0 – 8.2 | Chrome 64+, Firefox 69+, Safari 13.1+ and iOS Safari 13.4+
| Tracy 3.0 | PHP 8.0 – 8.2 | Chrome 64+, Firefox 69+, Safari 15.4+ and iOS Safari 15.4+
| Tracy 2.10| PHP 8.0 – 8.2 | Chrome 64+, Firefox 69+, Safari 15.4+ and iOS Safari 15.4+
| Tracy 2.9 | PHP 7.2 – 8.2 | Chrome 64+, Firefox 69+, Safari 13.1+ and iOS Safari 13.4+
| Tracy 2.8 | PHP 7.2 – 8.1 | Chrome 55+, Firefox 53+, Safari 11+ and iOS Safari 11+
| Tracy 2.7 | PHP 7.1 – 8.0 | Chrome 55+, Firefox 53+, MS Edge 16+, Safari 11+ and iOS Safari 11+
Expand Down Expand Up @@ -144,22 +145,22 @@ In order to detect misspellings when assigning to an object, we use [trait Nette
Content Security Policy
-----------------------

If your site uses Content Security Policy, you'll need to add `'nonce-<value>'` to `script-src` for Tracy to work properly. Some 3rd plugins may require additional directives.
If your site uses Content Security Policy, you'll need to add `'nonce-<value>'` and `'strict-dynamic'` to `script-src` for Tracy to work properly. Some 3rd plugins may require additional directives.
Nonce is not supported in the `style-src` directive, if you use this directive you need to add `'unsafe-inline'`, but this should be avoided in production mode.

Configuration example for [Nette Framework](https://nette.org):

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

Example in pure PHP:

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


Expand Down Expand Up @@ -264,6 +265,23 @@ If you use the Nette Framework, you can set this and others in the configuration
To protect your e-mail box from flood, Tracy sends **only one message** and creates a file `email-sent`. When a developer receives the e-mail notification, he checks the log, corrects his application and deletes the `email-sent` monitoring file. This activates the e-mail sending again.


Monolog integration
-------------------

This package provides a PSR-3 adapter, allowing for integration of [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'); // writes: [<TIMESTAMP>] main-channel.INFO: info [] []
Debugger::log('warning', Debugger::WARNING); // writes: [<TIMESTAMP>] main-channel.WARNING: warning [] []
```

Variable dumping
----------------

Expand Down Expand Up @@ -343,35 +361,6 @@ echo Debugger::timer(); // elapsed time in seconds
```


FireLogger
----------

You cannot always send debugging information to the browser window. This applies to AJAX requests or generating XML files to output. In such cases, you can send the messages by a separate channel into FireLogger. Error, Notice and Warning levels are sent to FireLogger window automatically. It is also possible to log suppressed exceptions in running application when attention to them is important.

How to do it?

- install extension [FireLogger for Chrome](https://chrome.google.com/webstore/detail/firelogger-for-chrome/hmagilfopmdjkeomnjpchokglfdfjfeh)
- turn on Chrome DevTools (using Ctrl-Shift-I key) and open Console

Navigate to the [demo page](https://examples.nette.org/tracy/) and you will see messages sent from PHP.

Because Tracy\Debugger communicates with FireLogger via HTTP headers, you must call the logging function before the PHP script sends anything to output. It is also possible to enable output buffering and delay the output.

```php
use Tracy\Debugger;

Debugger::fireLog('Hello World'); // send string into FireLogger console

Debugger::fireLog($_SERVER); // or even arrays and objects

Debugger::fireLog(new Exception('Test Exception')); // or exceptions
```

The result looks like this:

![FireLogger](https://nette.github.io/tracy/images/tracy-firelogger.png)


Custom Logger
-------------

Expand Down
74 changes: 1 addition & 73 deletions src/Bridges/Nette/Bridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,97 +9,25 @@

namespace Tracy\Bridges\Nette;

use Latte;
use Nette;
use Tracy;
use Tracy\BlueScreen;
use Tracy\Helpers;


/**
* Bridge for NEON & Latte.
* Bridge for NEON.
*/
class Bridge
{
public static function initialize(): void
{
$blueScreen = Tracy\Debugger::getBlueScreen();
if (!class_exists(Latte\Bridges\Tracy\BlueScreenPanel::class)) {
$blueScreen->addPanel([self::class, 'renderLatteError']);
$blueScreen->addAction([self::class, 'renderLatteUnknownMacro']);
$blueScreen->addFileGenerator(function (string $file) {
return substr($file, -6) === '.latte'
? "{block content}\n\$END\$"
: null;
});
Tracy\Debugger::addSourceMapper([self::class, 'mapLatteSourceCode']);
}

$blueScreen->addAction([self::class, 'renderMemberAccessException']);
$blueScreen->addPanel([self::class, 'renderNeonError']);
}


public static function renderLatteError(?\Throwable $e): ?array
{
if ($e instanceof Latte\CompileException && $e->sourceName) {
return [
'tab' => 'Template',
'panel' => (preg_match('#\n|\?#', $e->sourceName)
? ''
: '<p>'
. (@is_file($e->sourceName) // @ - may trigger error
? '<b>File:</b> ' . Helpers::editorLink($e->sourceName, $e->sourceLine)
: '<b>' . htmlspecialchars($e->sourceName . ($e->sourceLine ? ':' . $e->sourceLine : '')) . '</b>')
. '</p>')
. BlueScreen::highlightFile($e->sourceCode, $e->sourceLine, 15, false),
];
}

return null;
}


public static function renderLatteUnknownMacro(?\Throwable $e): ?array
{
if (
$e instanceof Latte\CompileException
&& $e->sourceName
&& @is_file($e->sourceName) // @ - may trigger error
&& (preg_match('#Unknown macro (\{\w+)\}, did you mean (\{\w+)\}\?#A', $e->getMessage(), $m)
|| preg_match('#Unknown attribute (n:\w+), did you mean (n:\w+)\?#A', $e->getMessage(), $m))
) {
return [
'link' => Helpers::editorUri($e->sourceName, $e->sourceLine, 'fix', $m[1], $m[2]),
'label' => 'fix it',
];
}

return null;
}


/** @return array{file: string, line: int, label: string, active: bool} */
public static function mapLatteSourceCode(string $file, int $line): ?array
{
if (!strpos($file, '.latte--')) {
return null;
}

$lines = file($file);
if (
!preg_match('#^/(?:\*\*|/) source: (\S+\.latte)#m', implode('', array_slice($lines, 0, 10)), $m)
|| !@is_file($m[1]) // @ - may trigger error
) {
return null;
}

$file = $m[1];
$line = $line && preg_match('#/\* line (\d+) \*/#', $lines[$line - 1], $m) ? (int) $m[1] : 0;
return ['file' => $file, 'line' => $line, 'label' => 'Latte', 'active' => true];
}


public static function renderMemberAccessException(?\Throwable $e): ?array
{
if (!$e instanceof Nette\MemberAccessException && !$e instanceof \LogicException) {
Expand Down
10 changes: 3 additions & 7 deletions src/Bridges/Nette/MailSender.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ class MailSender
{
use Nette\SmartObject;

/** @var Nette\Mail\IMailer */
private $mailer;
private Nette\Mail\IMailer $mailer;

/** @var string|null sender of email notifications */
private $fromEmail;
private ?string $fromEmail = null;


public function __construct(Nette\Mail\IMailer $mailer, ?string $fromEmail = null)
Expand All @@ -34,10 +33,7 @@ public function __construct(Nette\Mail\IMailer $mailer, ?string $fromEmail = nul
}


/**
* @param mixed $message
*/
public function send($message, string $email): void
public function send(mixed $message, string $email): void
{
$host = preg_replace('#[^\w.-]+#', '', $_SERVER['SERVER_NAME'] ?? php_uname('n'));

Expand Down
24 changes: 9 additions & 15 deletions src/Bridges/Nette/TracyExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,11 @@ class TracyExtension extends Nette\DI\CompilerExtension
{
private const ErrorSeverityPattern = 'E_(?:ALL|PARSE|STRICT|RECOVERABLE_ERROR|(?:CORE|COMPILE)_(?:ERROR|WARNING)|(?:USER_)?(?:ERROR|WARNING|NOTICE|DEPRECATED))';

/** @var bool */
private $debugMode;

/** @var bool */
private $cliMode;


public function __construct(bool $debugMode = false, bool $cliMode = false)
{
$this->debugMode = $debugMode;
$this->cliMode = $cliMode;
public function __construct(
private bool $debugMode = false,
private bool $cliMode = false,
) {
}


Expand Down Expand Up @@ -116,7 +110,7 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class)
];
$initialize->addBody($builder->formatPhp(
($tbl[$key] ?? 'Tracy\Debugger::$' . $key . ' = ?') . ';',
Nette\DI\Helpers::filterArguments([$value])
Nette\DI\Helpers::filterArguments([$value]),
));
}
}
Expand All @@ -130,14 +124,14 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class)
if ($this->debugMode) {
foreach ($this->config->bar as $item) {
if (is_string($item) && substr($item, 0, 1) === '@') {
$item = new Statement(['@' . $builder::THIS_CONTAINER, 'getService'], [substr($item, 1)]);
$item = new Statement(['@' . $builder::ThisContainer, 'getService'], [substr($item, 1)]);
} elseif (is_string($item)) {
$item = new Statement($item);
}

$initialize->addBody($builder->formatPhp(
'$this->getService(?)->addPanel(?);',
Nette\DI\Helpers::filterArguments([$this->prefix('bar'), $item])
Nette\DI\Helpers::filterArguments([$this->prefix('bar'), $item]),
));
}

Expand All @@ -154,7 +148,7 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class)
foreach ($this->config->blueScreen as $item) {
$initialize->addBody($builder->formatPhp(
'$this->getService(?)->addPanel(?);',
Nette\DI\Helpers::filterArguments([$this->prefix('blueScreen'), $item])
Nette\DI\Helpers::filterArguments([$this->prefix('blueScreen'), $item]),
));
}

Expand All @@ -171,7 +165,7 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class)
/**
* @param string|string[] $value
*/
private function parseErrorSeverity($value): int
private function parseErrorSeverity(string|array $value): int
{
$value = implode('|', (array) $value);
$res = (int) @parse_ini_string('e = ' . $value)['e']; // @ may fail
Expand Down
15 changes: 6 additions & 9 deletions src/Bridges/Psr/PsrToTracyLoggerAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,17 @@ class PsrToTracyLoggerAdapter implements Tracy\ILogger
Tracy\ILogger::CRITICAL => Psr\Log\LogLevel::CRITICAL,
];

/** @var Psr\Log\LoggerInterface */
private $psrLogger;


public function __construct(Psr\Log\LoggerInterface $psrLogger)
{
$this->psrLogger = $psrLogger;
public function __construct(
private Psr\Log\LoggerInterface $psrLogger,
) {
}


public function log($value, $level = self::INFO)
public function log(mixed $value, string $level = self::INFO)
{
if ($value instanceof \Throwable) {
$message = Tracy\Helpers::getClass($value) . ': ' . $value->getMessage() . ($value->getCode() ? ' #' . $value->getCode() : '') . ' in ' . $value->getFile() . ':' . $value->getLine();
$message = get_debug_type($value) . ': ' . $value->getMessage() . ($value->getCode() ? ' #' . $value->getCode() : '') . ' in ' . $value->getFile() . ':' . $value->getLine();
$context = ['exception' => $value];

} elseif (!is_string($value)) {
Expand All @@ -56,7 +53,7 @@ public function log($value, $level = self::INFO)
$this->psrLogger->log(
self::LevelMap[$level] ?? Psr\Log\LogLevel::ERROR,
$message,
$context
$context,
);
}
}
Loading