Monolog en PHP: handlers, processors, formatters y múltiples canales de log

Monolog es la librería de logging más popular en el ecosistema PHP. Implementa PSR-3 (LoggerInterface) y funciona mediante un sistema de capas: cada Logger tiene uno o varios Handlers que deciden dónde escribir los mensajes; los Processors añaden contexto automático a cada registro; y los Formatters determinan el formato de salida (texto, JSON…).

Instalación y logger básico

composer require monolog/monolog
<?php
use MonologLogger;
use MonologHandlerStreamHandler;
use MonologLevel;

// Crear un logger con nombre de canal
$log = new Logger('mi-app');

// Añadir un handler: escribe en fichero a partir del nivel WARNING
$log->pushHandler(new StreamHandler('/var/log/mi-app/app.log', Level::Warning));

// Añadir otro handler: escribe en stdout a partir del nivel DEBUG
$log->pushHandler(new StreamHandler('php://stdout', Level::Debug));

// Usar los 8 niveles PSR-3
$log->debug('Variable en memoria', ['valor' => 42]);
$log->info('Usuario autenticado', ['id' => 123]);
$log->warning('Espacio en disco bajo', ['libre_gb' => 1.2]);
$log->error('Fallo al enviar email', ['destinatario' => '[email protected]']);
$log->critical('Base de datos no disponible');

Apilar handlers

Monolog procesa los handlers en pila (el último añadido se ejecuta primero). Puedes combinar tantos como necesites:

<?php
use MonologHandlerStreamHandler;
use MonologHandlerRotatingFileHandler;
use MonologHandlerSyslogHandler;
use MonologHandlerSlackWebhookHandler;
use MonologLevel;

$log = new Logger('produccion');

// Fichero con rotación diaria, máx 30 días de histórico
$log->pushHandler(
    new RotatingFileHandler('/var/log/app/app.log', 30, Level::Debug)
);

// Syslog para integración con la pila de logs del sistema
$log->pushHandler(new SyslogHandler('mi-app', LOG_USER, Level::Notice));

// Slack para errores críticos (webhook URL en variable de entorno)
$log->pushHandler(
    new SlackWebhookHandler(
        webhookUrl:  getenv('SLACK_WEBHOOK'),
        channel:     '#alertas-prod',
        username:    'MonologBot',
        useAttachment: true,
        iconEmoji:   ':fire:',
        level:       Level::Critical
    )
);

Processors: contexto automático

Un Processor es un callable que recibe el array del registro y devuelve el array modificado. Úsalos para añadir automáticamente IP, ID de petición, usuario en sesión, etc. sin tener que hacerlo manualmente en cada llamada:

<?php
use MonologProcessorIntrospectionProcessor;
use MonologProcessorWebProcessor;
use MonologProcessorMemoryUsageProcessor;

$log = new Logger('api');

// Añade fichero y línea donde se llamó al logger
$log->pushProcessor(new IntrospectionProcessor());

// Añade URL, IP, método HTTP, usuario-agente
$log->pushProcessor(new WebProcessor());

// Añade uso de memoria en cada registro
$log->pushProcessor(new MemoryUsageProcessor());

// Processor personalizado: añadir el ID de la petición
$requestId = bin2hex(random_bytes(8));
$log->pushProcessor(function (array $record) use ($requestId): array {
    $record['extra']['request_id'] = $requestId;
    return $record;
});

$log->info('Petición recibida');
// Incluye: fichero, línea, URL, IP, método, request_id, memoria

Formatters: texto o JSON

<?php
use MonologFormatterLineFormatter;
use MonologFormatterJsonFormatter;
use MonologHandlerStreamHandler;

// Formato de línea personalizado
$handler = new StreamHandler('/var/log/app/app.log', Level::Debug);
$formatter = new LineFormatter(
    format:     "[%datetime%] %channel%.%level_name%: %message% %context% %extra%n",
    dateFormat: 'Y-m-d H:i:s.u',
    allowInlineLineBreaks: true,
    ignoreEmptyContextAndExtra: true,
);
$handler->setFormatter($formatter);

// Formato JSON (útil para ELK Stack, Grafana Loki, Datadog…)
$jsonHandler = new StreamHandler('php://stdout', Level::Debug);
$jsonHandler->setFormatter(new JsonFormatter());

$log = new Logger('app');
$log->pushHandler($handler);
$log->pushHandler($jsonHandler);

Múltiples canales de log

En proyectos grandes es útil tener un logger por canal (base de datos, emails, pagos…) para poder filtrar y analizar los logs por subsistema:

<?php
use MonologLogger;
use MonologHandlerRotatingFileHandler;
use MonologLevel;

function crearLogger(string $canal, string $fichero): Logger {
    $log = new Logger($canal);
    $log->pushHandler(new RotatingFileHandler($fichero, 30, Level::Debug));
    return $log;
}

$logBd      = crearLogger('bd',      '/var/log/app/bd.log');
$logEmail   = crearLogger('email',   '/var/log/app/email.log');
$logPagos   = crearLogger('pagos',   '/var/log/app/pagos.log');
$logSeguridad = crearLogger('seguridad', '/var/log/app/seguridad.log');

$logPagos->info('Pago recibido', ['importe' => 99.99, 'usuario_id' => 42]);
$logSeguridad->warning('Intento de acceso fallido', ['ip' => '1.2.3.4']);

Integración con frameworks

  • Laravel: Monolog es el backend de logging por defecto; se configura en config/logging.php.
  • Symfony: usa el componente Monolog Bundle; los canales se definen en config/packages/monolog.yaml.
  • Slim, Mezzio y otros: se registra Monolog en el contenedor DI como LoggerInterface.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP