Passer au contenu principal
Lorsqu’un package doit fournir sa propre sortie de logs, séparez le mécanisme qui construit le logger de la configuration de canal choisie par l’application consommatrice. Enregistrer un driver ne reconstruit pas les canaux existants, et d’anciens Handlers peuvent continuer à être utilisés même après une modification de la configuration. Cette page a été vérifiée sur l’implémentation de Laravel Framework v13.39.0. Pour les opérations de base, consultez Journalisation, et pour préparer le provider, consultez Développement de packages.

Choisir le point d’extension nécessaire

Si un driver existant suffit, il n’est pas nécessaire d’en ajouter un nouveau. Les classes via et tap sont résolues par le conteneur, l’injection par constructeur est donc disponible. tap est un ajustement effectué lors de la création du canal, et non un hook exécuté à chaque écriture d’une entrée de log.
Dans logging.channels.audit, audit est le nom du canal. Le driver qu’il contient est le nom de la méthode de création. Log::channel('audit') sélectionne un nom de canal : vous pouvez donc changer de driver plus tard tout en conservant le nom utilisé par l’appelant.

Exposer d’abord une factory avec custom et via

Dans un package fictif Acme\Audit, nous fournissons une factory pour la sortie vers un fichier. Placez-la dans src/Logging/CreateAuditLogger.php, chargeable via PSR-4.
Dans l’application consommatrice, ajoutez l’élément suivant au tableau channels existant de config/logging.php. Conservez les réglages existants de default et de stack.
LogManager::createCustomDriver() récupère la classe via depuis le conteneur et l’appelle avec le tableau de configuration du canal comme unique argument. Avec cette seule approche, aucun provider d’enregistrement de driver n’est nécessaire.
Cette factory transmet explicitement level au StreamHandler. LogManager n’applique pas uniformément le level de la configuration au logger renvoyé par une factory custom. Si vous ajoutez vos propres options de configuration, appliquez-les également dans la factory.
Pour simplement changer la destination du fichier, le driver single suffit. Cet exemple est la configuration minimale permettant d’intégrer un Handler ou une initialisation propres au package. Utilisez pour via un nom de classe pouvant être stocké dans le cache de configuration, et n’enregistrez ni closure ni instance de logger dans le tableau de configuration.

Utiliser extend pour fournir un nom de driver propre

Si vous souhaitez exposer une configuration driver: acme-audit à l’application consommatrice, enregistrez un driver utilisant la même factory. Étendez le service log de Laravel dans la méthode boot() de src/AuditServiceProvider.php.
Le provider est chargé, par exemple, par la découverte automatique de packages. Côté application, remplacez la configuration channels.audit précédente par la suivante. via n’est plus nécessaire.
  • Le LogServiceProvider de Laravel enregistre log en singleton. callAfterResolving() attend si le service n’est pas encore résolu, et applique aussi l’enregistrement à l’instance s’il l’est déjà. Pour plus de détails, consultez Hooks après résolution de service.
  • extend() ne fait qu’enregistrer la closure de création. Ni la création du canal ni l’écriture de logs n’ont lieu au moment de l’enregistrement de ce callback.
  • callCustomCreator() transmet $app et le tableau de configuration du canal à la closure. Ces arguments diffèrent de ceux transmis à une factory via.
  • Enregistrer à nouveau le même nom de driver écrase la closure de création. N’utilisez pas de noms intégrés comme single, ni de noms susceptibles d’entrer en conflit avec d’autres packages.
Dans l’implémentation vérifiée, une closure anonyme passée à extend() est re-liée à la portée de LogManager. Dans une closure non statique, $this n’est pas le service provider. Comme dans l’exemple, utilisez une closure statique avec l’argument $app, et ne dépendez pas du $this du provider.
Le package fournit le driver, et c’est l’application consommatrice qui choisit dans quel canal ou quelle stack l’utiliser. Concevez le provider de manière à ne pas réécrire logging.default ni l’ensemble des logging.channels existants.

Les canaux sont conservés par nom

LogManager::get() stocke les canaux résolus dans $channels. Lorsqu’un canal du même nom est demandé, l’objet conservé est renvoyé sans relire le tableau de configuration. Il s’agit d’une conservation d’objets au sein du LogManager courant, distincte du cache de configuration stocké dans un fichier. Modifier config() ou ré-enregistrer extend() ne suffit pas à remplacer les Handlers déjà créés.
Dans cet exemple, $same est le même objet que $before. Une fois que forgetChannel('audit') a libéré la conservation dans le Manager, la récupération suivante génère $after à partir de la nouvelle configuration.

forgetChannel ne met pas à jour les références existantes

forgetChannel() se contente de supprimer l’entrée dans le Manager.
  • Un service qui conserve $before peut continuer à utiliser l’ancien logger. Ce n’est pas une opération qui remplace les références existantes par le nouvel objet.
  • Une stack déjà générée, par exemple via Log::channel('stack'), possède son propre Monolog indépendant qui regroupe les Handlers et Processors des canaux enfants. Appeler forgetChannel() uniquement sur un enfant ne modifie pas la composition de la stack existante. Si nécessaire, libérez également la conservation du nom de canal de cette stack, puis récupérez-la à nouveau.
  • forgetChannel() n’appelle pas lui-même close() sur les Handlers. Cette API ne garantit pas la fermeture des connexions ni le vidage des buffers.
Lors d’un déploiement classique, intégrez à vos opérations la mise à jour du cache de configuration et le redémarrage des workers longue durée utilisés. Ne vous reposez pas sur un mécanisme qui réécrit la configuration et résout à nouveau les canaux à chaque requête, et tenez aussi compte du cycle de vie des services.

L’intégration dans une stack nécessite aussi des opérations compatibles Monolog

Un driver personnalisé peut renvoyer une implémentation de Psr\Log\LoggerInterface, mais createStackDriver() de Laravel appelle getHandlers() et getProcessors() sur les canaux enfants. Ces méthodes ne font pas partie de l’interface PSR. Renvoyer n’importe quel logger PSR ne garantit pas le fonctionnement dans une stack. Un package qui prend aussi en charge l’intégration dans une stack doit donc, comme dans l’exemple, renvoyer un Monolog, ou bien documenter explicitement le périmètre d’utilisation et le tester.

Vérifier la résolution réelle des canaux, pas seulement des mocks

Log::shouldReceive('channel') seul ne permet pas de vérifier l’enregistrement du provider, la sélection de la factory ni l’application de la configuration. L’exemple suivant fait passer les appels par le véritable Manager et la véritable factory dans l’environnement de test du package. Utilisez php://memory comme destination de test. Ici, on vérifie la construction du canal et la configuration du Handler, sans écrire de log.
LogManager::get() peut intercepter une exception survenue pendant la création d’un canal et renvoyer l’emergency logger. Constater qu’« aucune exception n’a été levée » ou que « c’était un LoggerInterface » ne prouve pas que le canal visé a bien été construit. Le test ci-dessus vérifie aussi le nom du Monolog, le Handler et le niveau de log, afin de ne pas confondre un repli avec un succès. Les erreurs d’écriture survenant après la création ne sont pas forcément interceptées par le même mécanisme.

Vérifications à ajouter pour la maintenance à long terme

Sources primaires consultées

Dernière modification le 28 octobre 2026