Skip to content

Commit

Permalink
Add roadrunner logger handler (#42)
Browse files Browse the repository at this point in the history
* Added support of roadrunner logger handler
  • Loading branch information
meekstellar authored Nov 9, 2022
1 parent 0310537 commit 3f5b0dd
Show file tree
Hide file tree
Showing 7 changed files with 296 additions and 0 deletions.
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protected const LOAD = [
RoadRunnerBridge\CommandBootloader::class,
RoadRunnerBridge\TcpBootloader::class, // Optional, if it needs to work with TCP plugin
RoadRunnerBridge\MetricsBootloader::class, // Optional, if it needs to work with metrics plugin
RoadRunnerBridge\LoggerBootloader::class, // Optional, if it needs to work with app-logger plugin
// ...
];
```
Expand Down Expand Up @@ -66,6 +67,9 @@ protected const LOAD = [
- [GRPC](#grpc)
- [Configuration](#configuration-4)
- [Commands](#console-commands-2)
- [Logger](#logger)
- [Configuration](#configuration-5)
- [Usage example](#usage-example-5)
- [Metrics](#metrics)

### Cache
Expand Down Expand Up @@ -1022,6 +1026,83 @@ Full example Echo GRPC service you can find [here](https://github.com/spiral/roa

------

### Logger
Logger provides a simple way to send log messages to RoadRunner.

## Configuration

You can register `Spiral\RoadRunnerBridge\Logger\Handler` in `app/config/monolog.php` config:

```php
<?php

declare(strict_types=1);

use Spiral\RoadRunnerBridge\Logger\Handler;

return [
//...

'handlers' => [
'roadrunner' => [
Handler::class,
// or
new Autowire(Handler::class, ['formatter' => "%message% foo"]),
]
]
];
```

Also you can define a default message format in `.env`
```dotenv
LOGGER_FORMAT=%message% foo
```

Handler registration example in a custom bootloader
```PHP
use Spiral\Boot\Bootloader;
use Spiral\Monolog\Bootloader\MonologBootloader;
use Spiral\RoadRunnerBridge\Logger\Handler;

final class SomeBootloader extends Bootloader
{
public function init(MonologBootloader $monolog, Handler $handler): void
{
$monolog->addHandler($handler);
}
}

```

## Usage
```PHP
<?php

declare(strict_types=1);

use Psr\Log\LoggerInterface;
use Spiral\Router\Annotation\Route;

class HomeController
{
#[Route(route: '/', name: 'home', methods: ['GET'])]
public function index(LoggerInterface $logger): string
{
$logger->warning('Warning message');
$logger->error('Error message');
$logger->debug('Debug message');
$logger->critical("Critical message");
$logger->info('Info message');
$logger->emergency("Emergency message");
}
}
```

Meanwhile in RoadRunner...
![image](https://user-images.githubusercontent.com/44509066/199532199-deba73a9-fca7-4098-afe1-0cb1fc1bf897.png)

------

### Roadrunner config example

```yaml
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"spiral/roadrunner-broadcast": "^2.0",
"spiral/roadrunner-tcp": "^2.0",
"spiral/roadrunner-metrics": "^2.0",
"roadrunner-php/app-logger": "^1.0",
"spiral/framework": "^3.2",
"spiral/serializer": "^3.2"
},
Expand Down
27 changes: 27 additions & 0 deletions src/Bootloader/LoggerBootloader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunnerBridge\Bootloader;

use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Boot\EnvironmentInterface;
use Spiral\Monolog\Bootloader\MonologBootloader;
use RoadRunner\Logger\Logger;
use Spiral\RoadRunnerBridge\Logger\Handler;

final class LoggerBootloader extends Bootloader
{
protected const DEPENDENCIES = [
MonologBootloader::class
];

protected const SINGLETONS = [
Handler::class => [self::class, 'initHandler']
];

private function initHandler(Logger $logger, EnvironmentInterface $env): Handler
{
return new Handler($logger, $env->get('LOGGER_FORMAT', Handler::FORMAT));
}
}
41 changes: 41 additions & 0 deletions src/Logger/Handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunnerBridge\Logger;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;
use RoadRunner\Logger\Logger as RoadRunnerLogger;

class Handler extends AbstractProcessingHandler
{
public const FORMAT = "%message% %context% %extra%\n";

public function __construct(
private readonly RoadRunnerLogger $logger,
string|FormatterInterface $formatter = self::FORMAT
) {
parent::__construct();

if (\is_string($formatter)) {
$formatter = new LineFormatter($formatter);
}

$this->setFormatter($formatter);
}

protected function write(array $record): void
{
$message = $record['formatted'];

match ($record['level']) {
Logger::ERROR, Logger::CRITICAL => $this->logger->error($message),
Logger::WARNING, Logger::ALERT, Logger::EMERGENCY => $this->logger->warning($message),
Logger::INFO, Logger::NOTICE => $this->logger->info($message),
Logger::DEBUG => $this->logger->debug($message),
};
}
}
16 changes: 16 additions & 0 deletions tests/src/Bootloader/LoggerBootloaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Spiral\Tests\Bootloader;

use Spiral\RoadRunnerBridge\Logger\Handler;
use Spiral\Tests\TestCase;

final class LoggerBootloaderTest extends TestCase
{
public function testHandlerBinding(): void
{
$this->assertContainerBoundAsSingleton(Handler::class, Handler::class);
}
}
129 changes: 129 additions & 0 deletions tests/src/Logger/HandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

declare(strict_types=1);

namespace Spiral\Tests\Logger;

use Psr\Log\LoggerInterface;
use RoadRunner\Logger\Logger;
use Spiral\Goridge\RPC\RPCInterface;
use Spiral\RoadRunnerBridge\Logger\Handler;
use Spiral\Tests\TestCase;
use Mockery as m;
use Monolog\Logger as Monolog;

final class HandlerTest extends TestCase
{
/**
* @dataProvider provideLogData
*/
public function testSendLog($expectedResult, $input): void
{
$rpc = m::mock(RPCInterface::class);

$rpc->shouldReceive('withServicePrefix')->once()->with('app')->andReturnSelf();

$monolog = new Monolog('default');
$monolog->setHandlers([
new Handler(
new Logger($rpc),
"%message% foo"
),
]);

$rpc->shouldReceive('call')
->once()
->with($expectedResult['level'], $expectedResult['message'])
->andReturnSelf();

$method = $input['method'];

$monolog->$method($input['message']);
}

public function provideLogData(): array
{
return [
[
[
'level' => 'Error',
'message' => 'Error message foo',
],
[
'method' => 'error',
'message' => 'Error message',
],
],
[
[
'level' => 'Warning',
'message' => 'Warning message foo',
],
[
'method' => 'warning',
'message' => 'Warning message',
],
],
[
[
'level' => 'Info',
'message' => 'Info message foo',
],
[
'method' => 'info',
'message' => 'Info message',
],
],
[
[
'level' => 'Debug',
'message' => 'Debug message foo',
],
[
'method' => 'debug',
'message' => 'Debug message',
],
],
[
[
'level' => 'Warning',
'message' => 'Emergency message foo',
],
[
'method' => 'emergency',
'message' => 'Emergency message',
],
],
[
[
'level' => 'Warning',
'message' => 'Alert message foo',
],
[
'method' => 'alert',
'message' => 'Alert message',
],
],
[
[
'level' => 'Error',
'message' => 'Critical message foo',
],
[
'method' => 'critical',
'message' => 'Critical message',
],
],
[
[
'level' => 'Info',
'message' => 'Notice message foo',
],
[
'method' => 'notice',
'message' => 'Notice message',
],
],
];
}
}
1 change: 1 addition & 0 deletions tests/src/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public function defineBootloaders(): array
RoadRunnerBridge\RoadRunnerBootloader::class,
RoadRunnerBridge\TcpBootloader::class,
RoadRunnerBridge\MetricsBootloader::class,
RoadRunnerBridge\LoggerBootloader::class,

// Framework commands
ConsoleBootloader::class,
Expand Down

0 comments on commit 3f5b0dd

Please sign in to comment.