Skip to content

Commit

Permalink
Logger: refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
f3l1x committed Jul 3, 2023
1 parent d58abf3 commit 3645013
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 76 deletions.
102 changes: 65 additions & 37 deletions .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ composer require nettrine/dbal

Register extension

```yaml
```neon
extensions:
nettrine.dbal: Nettrine\DBAL\DI\DbalExtension
```
Expand All @@ -50,7 +50,7 @@ This package relies on `doctrine/cache`, use prepared [nettrine/cache](https://g
composer require nettrine/cache
```

```yaml
```neon
extensions:
nettrine.cache: Nettrine\Cache\DI\CacheExtension
```
Expand All @@ -66,7 +66,7 @@ This package relies on `symfony/console`, use prepared [contributte/console](htt
composer require contributte/console
```

```yaml
```neon
extensions:
console: Contributte\Console\DI\ConsoleExtension(%consoleMode%)
Expand All @@ -83,13 +83,13 @@ Since this moment when you type `bin/console`, there'll be registered commands f

**Schema definition**

```yaml
```neon
nettrine.dbal:
debug:
panel: <boolean>
sourcePaths: <string[]>
configuration:
sqlLogger: <service>
middlewares: <service[]>
resultCache: <service>
filterSchemaAssetsExpression: <string>
autoCommit: <boolean>
Expand Down Expand Up @@ -118,7 +118,7 @@ nettrine.dbal:

Minimal configuration could look like this:

```yaml
```neon
nettrine.dbal:
debug:
panel: %debugMode%
Expand All @@ -138,7 +138,7 @@ Take a look at real **Nettrine DBAL** configuration example at [contributte/weba

Here is an example of how to register custom type for [UUID](https://github.com/ramsey/uuid-doctrine).

```yaml
```neon
dbal:
connection:
types:
Expand All @@ -165,56 +165,84 @@ Enable or disable Tracy panel via `debug.panel` key.
Alternatively, specify your application root path under the `debug.sourcePaths` key to display correct queries source map in Tracy panel.


### Events
### Middlewares

You can use native [Doctrine DBAL event system](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.10/reference/events.html#events).
> Since Doctrine v3.6 you have to use middlewares instead of event system, see issue [doctrine/dbal#5784](https://github.com/doctrine/dbal/issues/5784).
Create your subscriber class by implementing the `EventSubscriber` interface. Dependency injection with autowiring is enabled.
Middlewares are the way how to extend doctrine library or hook to special events.

```neon
dbal:
connection:
middlewares:
- MyMiddleware
```

```php
namespace App;
<?php

use Doctrine\Common\EventSubscriber;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Middleware;
use Tests\Fixtures\Driver\TestDriver;

final class PostConnectSubscriber implements EventSubscriber
final class MyMiddleware implements Middleware
{
public function postConnect(ConnectionEventArgs $args): void
{
// Magic goes here...
}
public function getSubscribedEvents(): array
{
return [Events::postConnect];
}

public function wrap(Driver $driver): Driver
{
return new MyDriver($driver);
}

}
```

Register your subscriber as a service in NEON file.
```php
<?php

```yaml
services:
subscriber1:
class: App\PostConnectSubscriber
```
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;

final class MyDriver extends AbstractDriverMiddleware
{

## Bridges
public function connect(array $params): Connection
{
return new MyConnection(parent::connect($params));
}

}
```

### PSR-3
### Logging

To log all queries with a PSR-3 logger, define the service under the `configuration.sqlLogger` key.
[Monolog](https://github.com/contributte/monolog) provides PSR compatible services.
> Since Doctrine v3.6 you have to use middlewares instead of event system, see issue [doctrine/dbal#5784](https://github.com/doctrine/dbal/issues/5784).
```yaml
To log all queries you should define your own middleware or you can use `Doctrine\DBAL\Logging\Middleware`.

```neon
dbal:
configuration:
sqlLogger: Nettrine\DBAL\Logger\PsrLogger()
middlewares:
logger: Doctrine\DBAL\Logging\Middleware(MyLogger())
```

You can try our prepared loggers.


```neon
dbal:
configuration:
middlewares:
# Write logs to file
logger: Doctrine\DBAL\Logging\Middleware(
Nettrine\DBAL\Logger\FileLogger(%tempDir%/db.sql)
)
# Show logs in tracy file
logger: Doctrine\DBAL\Logging\Middleware(
Nettrine\DBAL\Logger\TracyLogger
)
```

## Examples

Expand All @@ -224,7 +252,7 @@ dbal:
composer require nettrine/annotations nettrine/cache nettrine/migrations nettrine/fixtures nettrine/dbal nettrine/orm
```

```yaml
```neon
# Extension > Nettrine
# => order is crucial
#
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ parameters:
- .docs

ignoreErrors:
- '#.*deprecated class Doctrine\\DBAL\\Logging\\LoggerChain|SQLLogger.*#'
- '#Call to deprecated method formatPhp\(\) of class Nette\\DI\\ContainerBuilder#'
55 changes: 46 additions & 9 deletions src/DI/DbalExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Nette\DI\CompilerExtension;
use Nette\DI\Definitions\ServiceDefinition;
use Nette\DI\Definitions\Statement;
use Nette\PhpGenerator\ClassType;
use Nette\Schema\Expect;
use Nette\Schema\Schema;
use Nettrine\DBAL\ConnectionFactory;
use Nettrine\DBAL\Middleware\TracyMiddleware;
use Nettrine\DBAL\Tracy\BlueScreen\DbalBlueScreen;
use Nettrine\DBAL\Tracy\QueryPanel\QueryPanel;
use stdClass;
use Tracy\Bar;
use Tracy\BlueScreen;

/**
Expand All @@ -21,6 +25,8 @@
class DbalExtension extends CompilerExtension
{

public const TAG_MIDDLEWARE = 'nettrine.dbal.middleware';

public function getConfigSchema(): Schema
{
$expectService = Expect::anyOf(
Expand Down Expand Up @@ -80,13 +86,12 @@ public function loadDoctrineConfiguration(): void
->setAutowired(false);

// Middlewares
$middlewares = [];
foreach ($configurationConfig->middlewares as $middleware) {
$middlewares[] = new Statement($middleware);
foreach ($configurationConfig->middlewares as $name => $middleware) {
$builder->addDefinition($this->prefix('middleware.' . $name))
->setFactory($middleware)
->addTag(self::TAG_MIDDLEWARE);
}

$configuration->addSetup('setMiddlewares', [$middlewares]);

// ResultCache
$resultCache = $configurationConfig->resultCache !== null ? $builder->addDefinition($this->prefix('resultCache'))
->setFactory($configurationConfig->resultCache) : '@' . Cache::class;
Expand All @@ -104,6 +109,13 @@ public function loadDoctrineConfiguration(): void

// AutoCommit
$configuration->addSetup('setAutoCommit', [$configurationConfig->autoCommit]);

// Tracy middleware
if ($this->config->debug->panel) {
$builder->addDefinition($this->prefix('middleware.internal.tracy'))
->setFactory(TracyMiddleware::class)
->addTag(self::TAG_MIDDLEWARE);
}
}

public function loadConnectionConfiguration(): void
Expand All @@ -124,16 +136,41 @@ public function loadConnectionConfiguration(): void
]);
}

public function beforeCompile(): void
{
$builder = $this->getContainerBuilder();

// Set middlewares
$configurationDef = $builder->getDefinition($this->prefix('configuration'));
assert($configurationDef instanceof ServiceDefinition);

$configurationDef->addSetup('setMiddlewares', [
array_map(
fn (string $name) => $builder->getDefinition($name),
array_keys($builder->findByTag(self::TAG_MIDDLEWARE))
),
]);
}

public function afterCompile(ClassType $class): void
{
$builder = $this->getContainerBuilder();
$initialization = $this->getInitialization();

if ($this->config->debug->panel) {
$initialization->addBody('$this->getService(?)->addPanel(?);', [
$builder->getDefinitionByType(BlueScreen::class)->getName(),
[DbalBlueScreen::class, 'renderException'],
]);
$initialization->addBody(
$builder->formatPhp('?->addPanel(?);', [
$builder->getDefinitionByType(BlueScreen::class),
[DbalBlueScreen::class, 'renderException'],
])
);

$initialization->addBody(
$builder->formatPhp('?->addPanel(?);', [
$builder->getDefinitionByType(Bar::class),
new Statement(QueryPanel::class, [$builder->getDefinitionByType(TracyMiddleware::class)]),
])
);
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/Logger/FileLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Psr\Log\AbstractLogger;
use Stringable;

final class FileLogger extends AbstractLogger
class FileLogger extends AbstractLogger
{

public function __construct(
Expand All @@ -14,7 +14,10 @@ public function __construct(
{
}

public function log($level, Stringable|string $message, array $context = []): void
/**
* @param mixed[] $context
*/
public function log(mixed $level, Stringable|string $message, array $context = []): void
{
file_put_contents($this->file, sprintf('[%s] %s {%s}', date('d.m.Y H:i:s'), $message, json_encode($context)) . "\n", FILE_APPEND);
}
Expand Down
35 changes: 35 additions & 0 deletions src/Logger/SnapshotLogger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types = 1);

namespace Nettrine\DBAL\Logger;

use Psr\Log\AbstractLogger;
use Stringable;

class SnapshotLogger extends AbstractLogger
{

/** @var array<int, array{level: mixed, message: string, context: mixed[], timestamp: int}> */
protected array $snapshots = [];

/**
* @param mixed[] $context
*/
public function log(mixed $level, Stringable|string $message, array $context = []): void
{
$this->snapshots[] = [
'level' => $level,
'message' => (string) $message,
'context' => $context,
'timestamp' => time(),
];
}

/**
* @return array<int, array{duration:int}>
*/
public function getQueries(): array
{
return [];
}

}
7 changes: 5 additions & 2 deletions src/Logger/TracyLogger.php → src/Logger/TracyBarLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
use Tracy\Debugger;
use Tracy\Dumper;

class TracyLogger extends AbstractLogger
class TracyBarLogger extends AbstractLogger
{

public function log($level, Stringable|string $message, array $context = []): void
/**
* @param mixed[] $context
*/
public function log(mixed $level, Stringable|string $message, array $context = []): void
{
Debugger::barDump(
['message' => $message, 'context' => $context],
Expand Down
30 changes: 30 additions & 0 deletions src/Middleware/TracyMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace Nettrine\DBAL\Middleware;

use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Logging\Driver as LoggingDriver;
use Nettrine\DBAL\Logger\SnapshotLogger;

class TracyMiddleware implements Middleware
{

protected SnapshotLogger $logger;

public function __construct()
{
$this->logger = new SnapshotLogger();
}

public function wrap(Driver $driver): Driver
{
return new LoggingDriver($driver, $this->logger);
}

public function getLogger(): SnapshotLogger
{
return $this->logger;
}

}
Loading

0 comments on commit 3645013

Please sign in to comment.