Commit 10535580 authored by Fabien Potencier's avatar Fabien Potencier

made the logger more configurable

parent 55cb6a17
...@@ -28,6 +28,9 @@ Parameters ...@@ -28,6 +28,9 @@ Parameters
* **monolog.name** (optional): Name of the monolog channel, * **monolog.name** (optional): Name of the monolog channel,
defaults to ``myapp``. defaults to ``myapp``.
* **monolog.exception.logger_filter** (optional): An anonymous function that
filters which exceptions should be logged.
Services Services
-------- --------
...@@ -91,7 +94,7 @@ it by extending the ``monolog`` service:: ...@@ -91,7 +94,7 @@ it by extending the ``monolog`` service::
By default, all requests, responses and errors are logged by an event listener By default, all requests, responses and errors are logged by an event listener
registered as a service called `monolog.listener`. You can replace or remove registered as a service called `monolog.listener`. You can replace or remove
this service if you want to modify or disable the informations logged. this service if you want to modify or disable the logged information.
Traits Traits
------ ------
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
namespace Silex\EventListener; namespace Silex\EventListener;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent;
...@@ -24,15 +25,27 @@ use Symfony\Component\HttpFoundation\Response; ...@@ -24,15 +25,27 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RedirectResponse;
/** /**
* Log request, response and exceptions * Logs request, response, and exceptions.
*/ */
class LogListener implements EventSubscriberInterface class LogListener implements EventSubscriberInterface
{ {
protected $logger; protected $logger;
protected $exceptionLogFilter;
public function __construct(LoggerInterface $logger) public function __construct(LoggerInterface $logger, $exceptionLogFilter = null)
{ {
$this->logger = $logger; $this->logger = $logger;
if (null === $exceptionLogFilter) {
$exceptionLogFilter = function (\Exception $e) {
if ($e instanceof HttpExceptionInterface && $e->getStatusCode() < 500) {
return LogLevel::ERROR;
}
return LogLevel::CRITICAL;
};
}
$this->exceptionLogFilter = $exceptionLogFilter;
} }
/** /**
...@@ -80,7 +93,7 @@ class LogListener implements EventSubscriberInterface ...@@ -80,7 +93,7 @@ class LogListener implements EventSubscriberInterface
*/ */
protected function logRequest(Request $request) protected function logRequest(Request $request)
{ {
$this->logger->info('> '.$request->getMethod().' '.$request->getRequestUri()); $this->logger->log(LogLevel::DEBUG, '> '.$request->getMethod().' '.$request->getRequestUri());
} }
/** /**
...@@ -90,27 +103,18 @@ class LogListener implements EventSubscriberInterface ...@@ -90,27 +103,18 @@ class LogListener implements EventSubscriberInterface
*/ */
protected function logResponse(Response $response) protected function logResponse(Response $response)
{ {
$message = '< '.$response->getStatusCode();
if ($response instanceof RedirectResponse) { if ($response instanceof RedirectResponse) {
$this->logger->info('< '.$response->getStatusCode().' '.$response->getTargetUrl()); $message .= ' '.$response->getTargetUrl();
} else {
$this->logger->info('< '.$response->getStatusCode());
} }
$this->logger->log(LogLevel::DEBUG, $message);
} }
/**
* Logs an exception
*
* @param Exception $e
*/
protected function logException(\Exception $e) protected function logException(\Exception $e)
{ {
$message = sprintf('%s: %s (uncaught exception) at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()); $this->logger->log(call_user_func($this->exceptionLogFilter, $e), sprintf('%s: %s (uncaught exception) at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), array('exception' => $e));
if ($e instanceof HttpExceptionInterface && $e->getStatusCode() < 500) {
$this->logger->error($message, array('exception' => $e));
} else {
$this->logger->critical($message, array('exception' => $e));
}
} }
public static function getSubscribedEvents() public static function getSubscribedEvents()
......
...@@ -17,9 +17,6 @@ use Monolog\Logger; ...@@ -17,9 +17,6 @@ use Monolog\Logger;
use Monolog\Handler\StreamHandler; use Monolog\Handler\StreamHandler;
use Silex\Application; use Silex\Application;
use Silex\Api\BootableProviderInterface; use Silex\Api\BootableProviderInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bridge\Monolog\Handler\DebugHandler; use Symfony\Bridge\Monolog\Handler\DebugHandler;
use Silex\EventListener\LogListener; use Silex\EventListener\LogListener;
...@@ -69,12 +66,13 @@ class MonologServiceProvider implements ServiceProviderInterface, BootableProvid ...@@ -69,12 +66,13 @@ class MonologServiceProvider implements ServiceProviderInterface, BootableProvid
}; };
$app['monolog.listener'] = function () use ($app) { $app['monolog.listener'] = function () use ($app) {
return new LogListener($app['logger']); return new LogListener($app['logger'], $app['monolog.exception.logger_filter']);
}; };
$app['monolog.name'] = 'myapp'; $app['monolog.name'] = 'myapp';
$app['monolog.bubble'] = true; $app['monolog.bubble'] = true;
$app['monolog.permission'] = null; $app['monolog.permission'] = null;
$app['monolog.exception.logger_filter'] = null;
} }
public function boot(Application $app) public function boot(Application $app)
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
namespace Silex\Tests\EventListener; namespace Silex\Tests\EventListener;
use Psr\Log\LogLevel;
use Silex\EventListener\LogListener; use Silex\EventListener\LogListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
...@@ -34,8 +35,8 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase ...@@ -34,8 +35,8 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase
$logger = $this->getMock('Psr\\Log\\LoggerInterface'); $logger = $this->getMock('Psr\\Log\\LoggerInterface');
$logger $logger
->expects($this->once()) ->expects($this->once())
->method('info') ->method('log')
->with($this->equalTo('> GET /foo')) ->with(LogLevel::DEBUG, '> GET /foo')
; ;
$dispatcher = new EventDispatcher(); $dispatcher = new EventDispatcher();
...@@ -53,8 +54,8 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase ...@@ -53,8 +54,8 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase
$logger = $this->getMock('Psr\\Log\\LoggerInterface'); $logger = $this->getMock('Psr\\Log\\LoggerInterface');
$logger $logger
->expects($this->once()) ->expects($this->once())
->method('info') ->method('log')
->with($this->equalTo('< 301')) ->with(LogLevel::DEBUG, '< 301')
; ;
$dispatcher = new EventDispatcher(); $dispatcher = new EventDispatcher();
...@@ -71,15 +72,14 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase ...@@ -71,15 +72,14 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase
{ {
$logger = $this->getMock('Psr\\Log\\LoggerInterface'); $logger = $this->getMock('Psr\\Log\\LoggerInterface');
$logger $logger
->expects($this->once()) ->expects($this->at(0))
->method('critical') ->method('log')
->with($this->equalTo('RuntimeException: Fatal error (uncaught exception) at '.__FILE__.' line '.(__LINE__+14))) ->with(LogLevel::CRITICAL, 'RuntimeException: Fatal error (uncaught exception) at '.__FILE__.' line '.(__LINE__ + 13))
; ;
$logger $logger
->expects($this->once()) ->expects($this->at(1))
->method('error') ->method('log')
->with($this->equalTo('Symfony\Component\HttpKernel\Exception\HttpException: Http error (uncaught exception) at '.__FILE__.' line '.(__LINE__+10))) ->with(LogLevel::ERROR, 'Symfony\Component\HttpKernel\Exception\HttpException: Http error (uncaught exception) at '.__FILE__.' line '.(__LINE__ + 9))
; ;
$dispatcher = new EventDispatcher(); $dispatcher = new EventDispatcher();
...@@ -88,7 +88,6 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase ...@@ -88,7 +88,6 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase
$kernel = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); $kernel = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface');
$dispatcher->dispatch(KernelEvents::EXCEPTION, new GetResponseForExceptionEvent($kernel, Request::create('/foo'), HttpKernelInterface::SUB_REQUEST, new \RuntimeException('Fatal error'))); $dispatcher->dispatch(KernelEvents::EXCEPTION, new GetResponseForExceptionEvent($kernel, Request::create('/foo'), HttpKernelInterface::SUB_REQUEST, new \RuntimeException('Fatal error')));
$dispatcher->dispatch(KernelEvents::EXCEPTION, new GetResponseForExceptionEvent($kernel, Request::create('/foo'), HttpKernelInterface::SUB_REQUEST, new HttpException(400, 'Http error'))); $dispatcher->dispatch(KernelEvents::EXCEPTION, new GetResponseForExceptionEvent($kernel, Request::create('/foo'), HttpKernelInterface::SUB_REQUEST, new HttpException(400, 'Http error')));
} }
} }
...@@ -38,8 +38,8 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase ...@@ -38,8 +38,8 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase
$request = Request::create('/foo'); $request = Request::create('/foo');
$app->handle($request); $app->handle($request);
$this->assertTrue($app['monolog.handler']->hasInfo('> GET /foo')); $this->assertTrue($app['monolog.handler']->hasDebug('> GET /foo'));
$this->assertTrue($app['monolog.handler']->hasInfo('< 200')); $this->assertTrue($app['monolog.handler']->hasDebug('< 200'));
$this->assertTrue($app['monolog.handler']->hasInfo('Matched route "GET_foo" (parameters: "_controller": "{}", "_route": "GET_foo")')); $this->assertTrue($app['monolog.handler']->hasInfo('Matched route "GET_foo" (parameters: "_controller": "{}", "_route": "GET_foo")'));
} }
...@@ -108,7 +108,7 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase ...@@ -108,7 +108,7 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase
$request = Request::create('/foo'); $request = Request::create('/foo');
$app->handle($request); $app->handle($request);
$this->assertTrue($app['monolog.handler']->hasInfo('< 302 /bar')); $this->assertTrue($app['monolog.handler']->hasDebug('< 302 /bar'));
} }
public function testErrorLoggingGivesWayToSecurityExceptionHandling() public function testErrorLoggingGivesWayToSecurityExceptionHandling()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment