Symfony11 min

Symfony Logger : tout savoir sur Monolog dans Symfony

Par Pierre-Arthur Demengel
SymfonyMonologLoggerDebug

Les logs sont les yeux et les oreilles de votre application en production. Symfony integre Monolog, la librairie de logging la plus populaire en PHP, et la configure intelligemment selon l'environnement. Dans cet article, je vous montre comment configurer Monolog dans Symfony 7.2 pour couvrir tous les cas - du debug local aux alertes Slack en production.

Monolog dans Symfony : comment ca marche

Symfony utilise le bundle MonologBundle pour integrer la librairie Monolog dans le framework. Ce bundle fournit une configuration declarative en YAML et enregistre automatiquement le logger comme service injectable. L'architecture repose sur trois concepts cles :

  • Handlers - determinent ou les logs sont envoyes (fichier, console, Slack, email, etc.).
  • Processors - enrichissent chaque record de log avec des informations supplementaires.
  • Channels - separent les logs par domaine fonctionnel pour un routage independant.

L'installation est automatique avec Symfony Flex. Si besoin :

composer require symfony/monolog-bundle

Configuration de base par environnement

Symfony fournit des configurations differentes pour le dev et la prod. Voici la configuration de developpement type :

# config/packages/dev/monolog.yaml
monolog:
    handlers:
        main:
            type: stream
            path: "%kernel.logs_dir%/%kernel.environment%.log"
            level: debug
            channels: ["!event"]
        console:
            type: console
            process_psr_3_messages: false
            channels: ["!event", "!doctrine", "!console"]

En dev, tous les logs de niveau debug et au-dessus sont ecrits dans var/log/dev.log. Le channel event est exclu car il genere un volume enorme de logs a chaque requete. Le handler console affiche les logs directement dans le terminal quand vous executez des commandes.

La configuration de production est naturellement plus stricte :

# config/packages/prod/monolog.yaml
monolog:
    handlers:
        main:
            type: fingers_crossed
            action_level: error
            handler: nested
            excluded_http_codes: [404, 405]
            buffer_size: 50
        nested:
            type: rotating_file
            path: "%kernel.logs_dir%/%kernel.environment%.log"
            level: info
            max_files: 14
        console:
            type: console
            process_psr_3_messages: false
            channels: ["!event", "!doctrine"]

Le handler fingers_crossed

Le fingers_crossed est le handler le plus important en production. Son fonctionnement est ingenieux : il bufferise tous les logs de la requete en memoire sans les ecrire. Si une erreur de niveau error ou superieur survient, il vide tout le buffer vers le handler cible (nested). Sinon, les logs sont simplement jetes. Cela vous donne le contexte complet en cas d'erreur tout en gardant les fichiers de log propres.

Le parametre excluded_http_codes evite de logger les 404 classiques (pages non trouvees, bots) comme des erreurs. Le buffer_size limite la memoire utilisee. Pour gerer les taches de fond ou les erreurs asynchrones, pensez a configurer aussi Messenger avec ses workers.

Les niveaux de log PSR-3

Monolog implemente les 8 niveaux definis par la norme PSR-3. Le choix du bon niveau est crucial pour que vos logs soient exploitables :

NiveauUsageExemple
DEBUGInformation de debogageRequete SQL executee, cache hit/miss
INFOEvenement notableUtilisateur connecte, commande traitee
NOTICEEvenement normal mais significatifMigration executee, cache purge
WARNINGSituation anormale non bloquanteAPI tierce lente, deprecation
ERRORErreur d'executionPaiement echoue, fichier manquant
CRITICALComposant indisponibleBase de donnees injoignable
ALERTAction immediate requiseDisque plein, certificat expire
EMERGENCYSysteme inutilisableCorruption de donnees

Utiliser le logger dans vos services

Injectez LoggerInterface dans vos services via l'autowiring :

<?php

namespace App\Service;

use Psr\Log\LoggerInterface;

class PaymentService
{
    public function __construct(
        private readonly LoggerInterface $logger,
        private readonly PaymentGateway $gateway,
    ) {}

    public function processPayment(Order $order): PaymentResult
    {
        $this->logger->info('Debut du paiement', [
            'order_id' => $order->getId(),
            'amount' => $order->getTotal(),
            'currency' => 'EUR',
        ]);

        try {
            $result = $this->gateway->charge(
                $order->getTotal(),
                $order->getPaymentMethod()
            );

            $this->logger->info('Paiement reussi', [
                'order_id' => $order->getId(),
                'transaction_id' => $result->getTransactionId(),
            ]);

            return $result;

        } catch (PaymentException $e) {
            $this->logger->error('Echec du paiement', [
                'order_id' => $order->getId(),
                'error' => $e->getMessage(),
                'code' => $e->getCode(),
            ]);

            throw $e;
        }
    }
}

Le deuxieme parametre de chaque methode de log est un tableau de contexte. Utilisez-le systematiquement pour ajouter des donnees structurees - c'est bien plus exploitable qu'une simple chaine concatenee.

Channels : separer les logs par domaine

Les channels permettent de router differents types de logs vers differents handlers. Symfony cree automatiquement des channels pour ses composants (doctrine, security, request, etc.). Vous pouvez creer les votres :

# config/packages/monolog.yaml
monolog:
    channels: ['payment', 'import', 'notification']

    handlers:
        payment_file:
            type: rotating_file
            path: "%kernel.logs_dir%/payment.log"
            level: info
            channels: [payment]
            max_files: 30

        import_file:
            type: rotating_file
            path: "%kernel.logs_dir%/import.log"
            level: debug
            channels: [import]
            max_files: 7

Pour injecter un logger avec un channel specifique, utilisez l'attribut #[Target] en PHP 8.3 :

<?php

namespace App\Service;

use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Target;

class PaymentService
{
    public function __construct(
        #[Target('paymentLogger')]
        private readonly LoggerInterface $logger,
    ) {}
}

Ou configurez-le dans services.yaml :

# config/services.yaml
services:
    App\Service\PaymentService:
        arguments:
            $logger: '@monolog.logger.payment'

Handlers avances

Rotating file handler

Le handler rotating_file cree un nouveau fichier de log par jour et supprime automatiquement les anciens. Indispensable en production pour eviter un fichier unique de plusieurs Go :

handlers:
    main:
        type: rotating_file
        path: "%kernel.logs_dir%/%kernel.environment%.log"
        level: info
        max_files: 14  # garde 14 jours de logs

Handler Slack pour les alertes

Recevez les erreurs critiques directement dans Slack :

handlers:
    slack:
        type: slack_webhook
        webhook_url: "%env(SLACK_WEBHOOK_URL)%"
        channel: "#alertes-prod"
        bot_name: "MonApp Logger"
        icon_emoji: ":warning:"
        level: critical
        include_extra: true

Handler email avec le deduplication

handlers:
    deduplicated:
        type: deduplication
        handler: mail
        deduplication_level: error
        time: 60  # secondes de deduplication
    mail:
        type: symfony_mailer
        from_email: "logs@monapp.fr"
        to_email: "dev@monapp.fr"
        subject: "[PROD] Erreur critique"
        level: error
        content_type: text/html

Le handler deduplication evite de recevoir 500 emails si la meme erreur se produit en boucle. Il est recommande d'utiliser la CLI Symfony pour tester votre configuration de logs en local.

Processors : enrichir les logs

Les processors ajoutent automatiquement des informations a chaque record de log. Symfony en fournit plusieurs et vous pouvez creer les votres :

# config/packages/monolog.yaml
monolog:
    handlers:
        main:
            type: stream
            path: "%kernel.logs_dir%/%kernel.environment%.log"
            level: debug
            processors:
                - introspection   # ajoute fichier, ligne, classe, methode
                - web             # ajoute URL, IP, methode HTTP
                - memory_usage    # ajoute la memoire utilisee

Voici un processor custom qui ajoute l'utilisateur connecte a chaque log :

<?php

namespace App\Logger;

use Monolog\LogRecord;
use Monolog\Processor\ProcessorInterface;
use Symfony\Bundle\SecurityBundle\Security;

class UserProcessor implements ProcessorInterface
{
    public function __construct(
        private readonly Security $security,
    ) {}

    public function __invoke(LogRecord $record): LogRecord
    {
        $user = $this->security->getUser();

        if ($user !== null) {
            $record->extra['user_id'] = $user->getId();
            $record->extra['user_email'] = $user->getUserIdentifier();
        }

        return $record;
    }
}
# config/services.yaml
services:
    App\Logger\UserProcessor:
        tags:
            - { name: monolog.processor }

Debugging avec les logs en dev

En environnement de developpement, le Web Profiler de Symfony est votre meilleur ami. Cliquez sur l'icone de logs dans la barre de debug pour voir tous les logs de la requete courante, filtres par channel et niveau. Vous pouvez aussi examiner les logs dans la console lors de l'execution de commandes. Pour gerer les processus de fond, consultez notre article sur les machines a etats avec Workflow.

# Voir les logs en temps reel dans le terminal
tail -f var/log/dev.log

# Filtrer par niveau
tail -f var/log/dev.log | grep "ERROR"

# Avec la commande Symfony
symfony server:log

Structured logging avec le JSON formatter

Pour les outils de monitoring (ELK, Datadog, Grafana Loki), formatez vos logs en JSON :

handlers:
    main:
        type: stream
        path: "%kernel.logs_dir%/%kernel.environment%.log"
        level: info
        formatter: monolog.formatter.json

Cela produit des logs exploitables par des outils d'analyse automatique, avec chaque champ (message, level, channel, context, extra) indexable separement.

Bonnes pratiques

  • Utilisez le bon niveau - un ERROR doit vraiment etre une erreur, pas un cas metier normal.
  • Contexte structure - passez toujours un tableau de contexte, jamais de string concatenees.
  • Ne loggez pas de donnees sensibles - mots de passe, tokens, numeros de CB n'ont rien a faire dans les logs.
  • Surveillez la taille des logs - utilisez rotating_file et purgez regulierement.
  • Testez votre configuration - changez le niveau en dev pour verifier que les handlers fonctionnent.

Conclusion

Monolog dans Symfony est un systeme de logging complet et configurable. Le handler fingers_crossed, les channels dedies, les processors d'enrichissement et les alertes Slack forment un arsenal solide pour surveiller votre application. Investissez du temps dans cette configuration - en production, de bons logs font la difference entre un incident resolu en 5 minutes et un debug de 3 heures.

Vous souhaitez un audit de votre configuration Symfony ou un accompagnement technique ? Decouvrez mes services de developpement web, consultez les tarifs ou contactez-moi pour en discuter.

Questions fréquentes

13 projets livrésGrand-Est & BelgiqueLighthouse >90Disponible immédiatement

Un projet en tête ?

Discutons de votre site web. Réponse garantie sous 24h.

Ou appelez directement :06 95 41 30 25

WhatsApp
Appeler