Skip to content

Commit

Permalink
feature #28330 [MonologBridge] Add monolog processors adding route an…
Browse files Browse the repository at this point in the history
…d command info (trakos)

This PR was squashed before being merged into the 4.3-dev branch (closes #28330).

Discussion
----------

[MonologBridge] Add monolog processors adding route and command info

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        | symfony/symfony-docs#10244

This PR adds two simple processors that add context to every log entry.

RouteProcessor adds routing information:
`app.INFO: Some log text {"someContext":"ctx"} {"route":{"controller":"App\\Controller\\SomeController::number","route":"index","route_params":[]}`

ConsoleCommandProcessors adds current command information:
`app.INFO: Some log text {"someContext":"ctx"} {"command":{"name":"app:some-command","arguments":{"command":"app:some-command","some-argument":10}}}`

For ConsoleCommandProcessor I've decided against including command options by default, because there's a lot of default ones:
`"options":{"help":false,"quiet":false,"verbose":false,"version":false,"ansi":false,"no-ansi":false,"no-interaction":false,"env":"dev","no-debug":false}`. This behavior can be changed with a constructor argument.

Commits
-------

669f6b2 [MonologBridge] Add monolog processors adding route and command info
  • Loading branch information
fabpot committed Mar 17, 2019
2 parents cbb0b81 + 669f6b2 commit 6fa4d2b
Show file tree
Hide file tree
Showing 5 changed files with 398 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/Symfony/Bridge/Monolog/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========

4.3.0
-----

* added `ConsoleCommandProcessor`: monolog processor that adds command name and arguments
* added `RouteProcessor`: monolog processor that adds route name, controller::action and route params

4.2.0
-----

Expand Down
69 changes: 69 additions & 0 deletions src/Symfony/Bridge/Monolog/Processor/ConsoleCommandProcessor.php
@@ -0,0 +1,69 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Monolog\Processor;

use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\Service\ResetInterface;

/**
* Adds the current console command information to the log entry.
*
* @author Piotr Stankowski <git@trakos.pl>
*/
class ConsoleCommandProcessor implements EventSubscriberInterface, ResetInterface
{
private $commandData;
private $includeArguments;
private $includeOptions;

public function __construct(bool $includeArguments = true, bool $includeOptions = false)
{
$this->includeArguments = $includeArguments;
$this->includeOptions = $includeOptions;
}

public function __invoke(array $records)
{
if (null !== $this->commandData && !isset($records['extra']['command'])) {
$records['extra']['command'] = $this->commandData;
}

return $records;
}

public function reset()
{
$this->commandData = null;
}

public function addCommandData(ConsoleEvent $event)
{
$this->commandData = array(
'name' => $event->getCommand()->getName(),
);
if ($this->includeArguments) {
$this->commandData['arguments'] = $event->getInput()->getArguments();
}
if ($this->includeOptions) {
$this->commandData['options'] = $event->getInput()->getOptions();
}
}

public static function getSubscribedEvents()
{
return array(
ConsoleEvents::COMMAND => array('addCommandData', 1),
);
}
}
86 changes: 86 additions & 0 deletions src/Symfony/Bridge/Monolog/Processor/RouteProcessor.php
@@ -0,0 +1,86 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Monolog\Processor;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Contracts\Service\ResetInterface;

/**
* Adds the current route information to the log entry.
*
* @author Piotr Stankowski <git@trakos.pl>
*/
class RouteProcessor implements EventSubscriberInterface, ResetInterface
{
private $routeData;
private $includeParams;

public function __construct(bool $includeParams = true)
{
$this->includeParams = $includeParams;
$this->reset();
}

public function __invoke(array $records)
{
if ($this->routeData && !isset($records['extra']['requests'])) {
$records['extra']['requests'] = array_values($this->routeData);
}

return $records;
}

public function reset()
{
$this->routeData = array();
}

public function addRouteData(GetResponseEvent $event)
{
if ($event->isMasterRequest()) {
$this->reset();
}

$request = $event->getRequest();
if (!$request->attributes->has('_controller')) {
return;
}

$currentRequestData = array(
'controller' => $request->attributes->get('_controller'),
'route' => $request->attributes->get('_route'),
);

if ($this->includeParams) {
$currentRequestData['route_params'] = $request->attributes->get('_route_params');
}

$this->routeData[spl_object_id($request)] = $currentRequestData;
}

public function removeRouteData(FinishRequestEvent $event)
{
$requestId = spl_object_id($event->getRequest());
unset($this->routeData[$requestId]);
}

public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array('addRouteData', 1),
KernelEvents::FINISH_REQUEST => array('removeRouteData', 1),
);
}
}
@@ -0,0 +1,75 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Monolog\Tests\Processor;

use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Monolog\Processor\ConsoleCommandProcessor;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Input\InputInterface;

class ConsoleCommandProcessorTest extends TestCase
{
private const TEST_ARGUMENTS = array('test' => 'argument');
private const TEST_OPTIONS = array('test' => 'option');
private const TEST_NAME = 'some:test';

public function testProcessor()
{
$processor = new ConsoleCommandProcessor();
$processor->addCommandData($this->getConsoleEvent());

$record = $processor(array('extra' => array()));

$this->assertArrayHasKey('command', $record['extra']);
$this->assertEquals(
array('name' => self::TEST_NAME, 'arguments' => self::TEST_ARGUMENTS),
$record['extra']['command']
);
}

public function testProcessorWithOptions()
{
$processor = new ConsoleCommandProcessor(true, true);
$processor->addCommandData($this->getConsoleEvent());

$record = $processor(array('extra' => array()));

$this->assertArrayHasKey('command', $record['extra']);
$this->assertEquals(
array('name' => self::TEST_NAME, 'arguments' => self::TEST_ARGUMENTS, 'options' => self::TEST_OPTIONS),
$record['extra']['command']
);
}

public function testProcessorDoesNothingWhenNotInConsole()
{
$processor = new ConsoleCommandProcessor(true, true);

$record = $processor(array('extra' => array()));
$this->assertEquals(array('extra' => array()), $record);
}

private function getConsoleEvent(): ConsoleEvent
{
$input = $this->getMockBuilder(InputInterface::class)->getMock();
$input->method('getArguments')->willReturn(self::TEST_ARGUMENTS);
$input->method('getOptions')->willReturn(self::TEST_OPTIONS);
$command = $this->getMockBuilder(Command::class)->disableOriginalConstructor()->getMock();
$command->method('getName')->willReturn(self::TEST_NAME);
$consoleEvent = $this->getMockBuilder(ConsoleEvent::class)->disableOriginalConstructor()->getMock();
$consoleEvent->method('getCommand')->willReturn($command);
$consoleEvent->method('getInput')->willReturn($input);

return $consoleEvent;
}
}

0 comments on commit 6fa4d2b

Please sign in to comment.