Monolog es una biblioteca para bitácora de eventos en PHP 5.3 utilizada por Symfony2. Inspirada en la biblioteca LogBook de Python.
Para registrar un mensaje, simplemente consigue el servicio de registro cronológico del contenedor en tu controlador:
public function indexAction()
{
$logger = $this->get('logger');
$logger->info('I just got the logger');
$logger->err('An error occurred');
// ...
}
El servicio logger tiene diferentes métodos para diferentes niveles de registro cronológico de eventos. Ve la Symfony\Component\HttpKernel\Log\LoggerInterface para detalles de cuáles métodos están disponibles.
En Monolog cada cual registrador define un canal para su registro cronológico, el cual organiza tus mensajes de registro en diferentes «categorías». Luego, cada canal tiene una pila de manejadores para escribir los registros (los manejadores se pueden compartir).
Truco
Cuándo inyectas el registrador en un servicio puedes usar un canal personalizado para controlar en qué «canal» deberá llevar el registro cronológico de eventos.
El manipulador básico es el StreamHandler el cual escribe los registros en un flujo (por omisión en app/logs/prod.log en el entorno de producción y app/logs/dev.log en el entorno de desarrollo).
Monolog también viene con una potente capacidad integrada para encargarse del registro cronológico de eventos en el entorno de producción: FingersCrossedHandler. Esta te permite almacenar los mensajes en la memoria intermedia y registrarla sólo si un mensaje llega al nivel de acción (ERROR en la configuración prevista en la edición estándar) transmitiendo los mensajes a otro manipulador.
El registro cronológico de eventos utiliza una pila de manejadores que se van llamando sucesivamente. Esto te permite registrar los mensajes de varias formas fácilmente.
# app/config/config.yml
monolog:
handlers:
applog:
type: stream
path: /var/log/symfony.log
level: error
main:
type: fingers_crossed
action_level: warning
handler: file
file:
type: stream
level: debug
syslog:
type: syslog
level: error
<!-- app/config/config.xml -->
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:monolog="http://symfony.com/schema/dic/monolog"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/monolog http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">
<monolog:config>
<monolog:handler
name="applog"
type="stream"
path="/var/log/symfony.log"
level="error"
/>
<monolog:handler
name="main"
type="fingers_crossed"
action-level="warning"
handler="file"
/>
<monolog:handler
name="file"
type="stream"
level="debug"
/>
<monolog:handler
name="syslog"
type="syslog"
level="error"
/>
</monolog:config>
</container>
// app/config/config.php
$container->loadFromExtension('monolog', array(
'handlers' => array(
'applog' => array(
'type' => 'stream',
'path' => '/var/log/symfony.log',
'level' => 'error',
),
'main' => array(
'type' => 'fingers_crossed',
'action_level' => 'warning',
'handler' => 'file',
),
'file' => array(
'type' => 'stream',
'level' => 'debug',
),
'syslog' => array(
'type' => 'syslog',
'level' => 'error',
),
),
));
La configuración anterior define una pila de controladores que se llamarán en el orden en el cual se definen.
Truco
El controlador denominado «file» no se incluirá en la misma pila que se utiliza como un controlador anidado del controlador fingers_crossed.
Nota
Si deseas cambiar la configuración de MonologBundle en otro archivo de configuración necesitas redefinir toda la pila. No se pueden combinar, ya que el orden es importante y una combinación no te permite controlar el orden.
El controlador utiliza un formateador para dar formato al registro del evento antes de ingresarlo. Todos los manipuladores Monolog utilizan una instancia de Monolog\Formatter\LineFormatter por omisión, pero la puedes reemplazar fácilmente. Tu formateador debe implementar Monolog\Formatter\FormatterInterface.
# app/config/config.yml
services:
my_formatter:
class: Monolog\Formatter\JsonFormatter
monolog:
handlers:
file:
type: stream
level: debug
formatter: my_formatter
<!-- app/config/config.xml -->
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:monolog="http://symfony.com/schema/dic/monolog"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/monolog http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">
<services>
<service id="my_formatter" class="Monolog\Formatter\JsonFormatter" />
</services>
<monolog:config>
<monolog:handler
name="file"
type="stream"
level="debug"
formatter="my_formatter"
/>
</monolog:config>
</container>
// app/config/config.php
$container
->register('my_formatter', 'Monolog\Formatter\JsonFormatter');
$container->loadFromExtension('monolog', array(
'handlers' => array(
'file' => array(
'type' => 'stream',
'level' => 'debug',
'formatter' => 'my_formatter',
),
),
));
Monolog te permite procesar el registro del evento antes de ingresarlo añadiendo algunos datos adicionales. Un procesador se puede aplicar al controlador de toda la pila o sólo a un controlador específico.
Un procesador simplemente es un ejecutable que recibe el registro como primer argumento.
Los procesadores se configuran usando la etiqueta DIC monolog.processor. Consulta la referencia sobre esto.
A veces es difícil saber cuál de las entradas en el registro pertenece a cada sesión y/o petición. En el siguiente ejemplo agregaremos una ficha única para cada petición usando un procesador.
namespace Acme\MiBundle;
use Symfony\Component\HttpFoundation\Session\Session;
class SessionRequestProcessor
{
private $session;
private $token;
public function __construct(Session $session)
{
$this->session = $session;
}
public function processRecord(array $record)
{
if (null === $this->token) {
try {
$this->token = substr($this->session->getId(), 0, 8);
} catch (\RuntimeException $e) {
$this->token = '????????';
}
$this->token .= '-' . substr(uniqid(), -8);
}
$record['extra']['token'] = $this->token;
return $record;
}
}
# app/config/config.yml
services:
monolog.formatter.session_request:
class: Monolog\Formatter\LineFormatter
arguments:
- "[%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%%\n"
monolog.processor.session_request:
class: Acme\MyBundle\SessionRequestProcessor
arguments: ["@session"]
tags:
- { name: monolog.processor, method: processRecord }
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
formatter: monolog.formatter.session_request
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:monolog="http://symfony.com/schema/dic/monolog"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/monolog http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">
<services>
<service id="monolog.formatter.session_request" class="Monolog\Formatter\LineFormatter">
<argument>[%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%%\n</argument>
</service>
<service id="monolog.processor.session_request" class="Acme\MyBundle\SessionRequestProcessor">
<argument type="service" id="session" />
<tag name="monolog.processor" method="processRecord" />
</service>
</services>
<monolog:config>
<monolog:handler
name="main"
type="stream"
path="%kernel.logs_dir%/%kernel.environment%.log"
level="debug"
formatter="monolog.formatter.session_request"
/>
</monolog:config>
</container>
// app/config/config.php
$container
->register('monolog.formatter.session_request', 'Monolog\Formatter\LineFormatter')
->addArgument('[%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%%\n');
$container
->register('monolog.processor.session_request', 'Acme\MyBundle\SessionRequestProcessor')
->addArgument(new Reference('session'))
->addTag('monolog.processor', array('method' => 'processRecord'));
$container->loadFromExtension('monolog', array(
'handlers' => array(
'main' => array(
'type' => 'stream',
'path' => '%kernel.logs_dir%/%kernel.environment%.log',
'level' => 'debug',
'formatter' => 'monolog.formatter.session_request',
),
),
));
Nota
Si utilizas varios manipuladores, también puedes registrar el procesador a nivel del manipulador en lugar de globalmente.