Saltar al contenido principal
Cuando un paquete quiere ofrecer su propia salida de logs, conviene separar el mecanismo que crea el logger de la configuración de canales que elige la aplicación que usa el paquete. Registrar un driver no vuelve a crear los canales existentes, y puede que se siga usando el Handler antiguo incluso después de cambiar la configuración. Esta página se basa en la revisión de la implementación de Laravel Framework v13.39.0. Para el uso básico, consulta Logging, y para preparar el provider, Desarrollo de paquetes.

Elige el punto de extensión que necesitas

Si puedes conseguirlo con un driver existente, no hace falta añadir un driver propio. Las clases de via y de tap se resuelven desde el contenedor, así que también puedes usar inyección por constructor. tap es un ajuste que se realiza al generar el canal, no un hook que se ejecute cada vez que se escribe una entrada de log.
En logging.channels.audit, audit es el nombre del canal. El driver que contiene es el nombre del método de creación. Lo que se elige con Log::channel('audit') es el nombre del canal, por lo que aunque cambies el driver más adelante, el código que lo llama puede mantener el mismo nombre.

Publica primero una factory con custom y via

En el paquete ficticio Acme\Audit, ofreceremos una factory para la salida a archivo. Colócala en src/Logging/CreateAuditLogger.php, que se carga mediante PSR-4.
En la aplicación que usa el paquete, añade el siguiente elemento al array channels existente de config/logging.php. Conserva la configuración existente de default y stack.
LogManager::createCustomDriver() obtiene la clase de via desde el contenedor y la llama con el array de configuración del canal como único argumento. Si solo usas este método, no necesitas un provider para registrar el driver.
Esta factory pasa level explícitamente al StreamHandler. LogManager no aplica de forma general el level de la configuración al logger que devuelve una factory custom. Si añades opciones de configuración propias, también debes aplicarlas en la factory.
Si solo quieres cambiar el archivo de destino, basta con el driver single. Este ejemplo es la configuración mínima para incorporar un Handler o una inicialización propios del paquete. Usa en via un nombre de clase que pueda almacenarse en la caché de configuración, y no guardes closures ni instancias de logger en el array de configuración.

Usa extend cuando ofrezcas un nombre de driver propio

Si quieres publicar para la aplicación una configuración como driver: acme-audit, registra un driver que use la misma factory. Extiende el servicio log de Laravel en el boot() de src/AuditServiceProvider.php.
El provider se carga, por ejemplo, mediante el descubrimiento automático de paquetes. En la aplicación, sustituye la configuración anterior de channels.audit por la siguiente. via ya no es necesario.
  • El LogServiceProvider de Laravel registra log como singleton. callAfterResolving() espera si todavía no se ha resuelto y, si ya está resuelto, aplica el registro también a esa instancia. Para más detalles, consulta Hooks posteriores a la resolución de servicios.
  • extend() solo registra el closure de creación. Al registrar este callback no se genera ningún canal ni se escribe ningún log.
  • callCustomCreator() pasa al closure $app y el array de configuración del canal. Son argumentos distintos de los que recibe la factory de via.
  • Registrar de nuevo el mismo nombre de driver sobrescribe el closure de creación. No uses nombres integrados como single ni nombres que puedan entrar en conflicto con otros paquetes.
En la implementación revisada, el closure anónimo pasado a extend() se vuelve a vincular al ámbito de LogManager. En un closure no estático, $this no es el service provider. Como en el ejemplo, usa un closure estático con el argumento $app y no dependas del $this del provider.
El paquete proporciona el driver, y es la aplicación la que elige en qué canal o stack usarlo. Diseña el provider de forma que no reescriba logging.default ni todo el logging.channels existente.

Los canales se retienen por nombre

LogManager::get() guarda los canales resueltos en $channels. Al obtener un canal con el mismo nombre, devuelve el objeto retenido sin volver a leer el array de configuración. Esto es la retención de objetos dentro del LogManager actual, distinta de la caché de configuración que se guarda en un archivo. Cambiar config() o volver a registrar extend() no sustituye por sí solo los Handlers ya creados.
En este ejemplo, $same es el mismo objeto que $before. Cuando forgetChannel('audit') libera la retención en el Manager, la siguiente obtención genera $after a partir de la nueva configuración.

forgetChannel no actualiza las referencias existentes

Lo que hace forgetChannel() es eliminar la entrada dentro del Manager.
  • Los servicios que conservan $before pueden seguir usando el logger antiguo. No es una operación que sustituya las referencias existentes por el nuevo objeto.
  • Un stack ya generado, por ejemplo con Log::channel('stack'), tiene su propio Monolog independiente que reúne los Handlers y Processors de los canales hijos. Aplicar forgetChannel() solo al hijo no cambia la composición del stack existente. Si es necesario, libera también la retención del nombre de canal de ese stack y vuelve a obtenerlo.
  • forgetChannel() por sí mismo no llama a close() de los Handlers. No es una API que garantice el cierre de conexiones ni el vaciado de buffers.
En los despliegues habituales, incluye en la operación la actualización de la caché de configuración y el reinicio de los workers de larga duración que utilices. No dependas de un mecanismo que reescriba la configuración y vuelva a resolver los canales en cada solicitud, y ten en cuenta también el ciclo de vida de los servicios.

Para incorporarlo a un stack también se necesitan operaciones compatibles con Monolog

Un driver propio puede devolver una implementación de Psr\Log\LoggerInterface, pero createStackDriver() de Laravel llama a getHandlers() y getProcessors() en los canales hijos. Estos métodos no forman parte de la interfaz PSR. Devolver cualquier logger PSR no garantiza que funcione dentro de un stack, así que en los paquetes que también admitan su incorporación a un stack, devuelve un Monolog como en el ejemplo, o indica claramente el alcance de uso y pruébalo.

Verifica la resolución real de canales, no solo con mocks

Con Log::shouldReceive('channel') por sí solo no puedes comprobar el registro del provider, la selección de la factory ni la aplicación de la configuración. A continuación se muestra un ejemplo que pasa por el Manager y la factory reales en el entorno de pruebas de paquetes. Como destino de salida para las pruebas se indica php://memory. Aquí se verifican la construcción del canal y la configuración del Handler, sin escribir realmente ningún log.
LogManager::get() puede capturar las excepciones producidas durante la generación del canal y devolver el emergency logger. Comprobar solo que «no se lanzó ninguna excepción» o que «era un LoggerInterface» no demuestra que se haya construido el canal deseado. La prueba anterior verifica también el nombre del Monolog, el Handler y el nivel de log para no confundir el fallback con un éxito. Los errores de escritura posteriores a la generación no siempre se capturan con el mismo mecanismo.

Verificaciones adicionales para el mantenimiento a largo plazo

Fuentes primarias consultadas

Última modificación el 28 de octubre de 2026