Configuration avancée de la gestion des logs avec Spring Boot et Logback

Spring Boot intègre une fonctionnalité de journalisation par défaut. Cependant, pour des besoins plus avancés tels que la sortie personnalisée des logs sur disque, l'archivage quotidien, ou la rotation des fichiers, la configuration intégrée peut s'avérer limitée. Cet article détaille comment étendre cette fonctionnalité en utilisant Logback, un framework de journalisation popluaire.

Le parent de votre projet Spring Boot, spring-boot-starter-parent, inclut déjà les dépendances nécessaires pour la journalisation, notamment spring-boot-starter-logging. Il est donc inutile d'ajouter manuellement des dépendences pour ch.qos.logback:logback-classic, org.apache.logging.log4j:log4j-to-slf4j, ou org.slf4j:jul-to-slf4j, car elles sont gérées par le bom (Bill Of Materials) de Spring Boot.

Pour une configuration personnalisée, il est recommandé de créer un fichier logback-spring.xml dans le répertoire src/main/resources. L'utilisation du suffixe "-spring" permet de bénéficier de fonctionnalités spécifiques à Spring, comme l'utilisation de profils via la balise <springProfile>.

Voici un exemple de configuration logback-spring.xml détaillée :


<?xml version="1.0" encoding="UTF-8"?>
<!-- Les niveaux de journalisation, du plus bas au plus élevé, sont : TRACE, DEBUG, INFO, WARN, ERROR, FATAL.
     Si le niveau est défini sur WARN, les messages inférieurs à WARN ne seront pas affichés. -->
<!-- scan: Si cet attribut est défini sur 'true', le fichier de configuration sera rechargé en cas de modification. Par défaut, il est à 'true'. -->
<!-- scanPeriod: Définit l'intervalle de temps pour vérifier les modifications du fichier de configuration. L'unité par défaut est la milliseconde. Cet attribut est actif lorsque 'scan' est 'true'. L'intervalle par défaut est de 1 minute. -->
<!-- debug: Si cet attribut est défini sur 'true', les messages internes de Logback seront affichés pour surveiller son état de fonctionnement. Par défaut, il est à 'false'. -->
<configuration  scan="true" scanPeriod="10 seconds">

    <!-- Vous pouvez inclure la configuration par défaut de Spring Boot si nécessaire -->
    <!-- <include resource="org/springframework/boot/logging/logback/base.xml" /> -->

    <contextName>logback</contextName>
    <!-- Le nom est la variable, la valeur est sa définition. Les variables sont utilisées avec "${}" -->
    <property name="log.path" value="logs/application" /> <!-- Chemin de base pour les logs -->

    <!-- Configuration pour les logs en couleur sur la console -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:- %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />

    <!-- Appender pour la sortie console -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!-- Niveau minimum pour la console (ex: INFO) -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- Appender pour les logs DEBUG (rotation par date et taille) -->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/debug.log</file> <!-- Fichier courant -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- Politique d'archivage : nom du fichier basé sur la date et un identifiant unique -->
            <fileNamePattern>${log.path}/archive/debug-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- Politique de déclenchement basée sur la taille et le temps -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- Taille maximale du fichier avant rotation -->
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- Nombre de jours de conservation des logs archivés -->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- Filtre pour ne capturer que les logs de niveau DEBUG -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- Appender pour les logs INFO (rotation par date et taille) -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/info.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/archive/info-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- Filtre pour ne capturer que les logs de niveau INFO -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- Appender pour les logs WARN (rotation par date et taille) -->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/warn.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/archive/warn-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- Filtre pour ne capturer que les logs de niveau WARN -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- Appender pour les logs ERROR (rotation par date et taille) -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/error.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/archive/error-log-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- Filtre pour ne capturer que les logs de niveau ERROR -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--
        La balise <logger> permet de configurer le niveau de journalisation pour des paquets ou des classes spécifiques,
        et d'associer des appenders.
        'name': Spécifie le paquet ou la classe concernée.
        'level': Définit le niveau de journalisation (TRACE, DEBUG, INFO, WARN, ERROR, ALL, OFF).
                 'INHERITED' ou 'NULL' signifie qu'il hérite du niveau du logger parent.
        'addtivity': Indique si le message doit être transmis au logger parent (par défaut 'true').
    -->
    <!-- Exemple : Configurer le niveau DEBUG pour le paquet 'com.example.myproject' -->
    <!-- <logger name="com.example.myproject" level="debug"/> -->

    <!--
        La balise <root> est obligatoire et définit le niveau de journalisation global.
        Elle peut contenir zéro ou plusieurs balises <appender-ref> pour associer des appenders.
    -->

    <!-- Configuration pour l'environnement de développement (dev) : logs en console et fichiers -->
    <springProfile name="dev">
        <logger name="com.nmys.view" level="debug"/> <!-- Exemple de configuration spécifique pour une classe/paquet -->
        <root level="debug"> <!-- Niveau de base DEBUG pour le développement -->
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="DEBUG_FILE" />
            <appender-ref ref="INFO_FILE" />
            <appender-ref ref="WARN_FILE" />
            <appender-ref ref="ERROR_FILE" />
        </root>
    </springProfile>

    <!-- Configuration par défaut (si aucun profil n'est actif ou pour production) : logs en console et fichiers -->
    <root level="info"> <!-- Niveau INFO par défaut -->
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="WARN_FILE" />
        <appender-ref ref="ERROR_FILE" />
        <!-- Inclure DEBUG_FILE si nécessaire même en production, mais avec un niveau de base INFO -->
        <appender-ref ref="DEBUG_FILE" />
    </root>

</configuration>

Pour que Spring Boot charge ce fichier de configuration, vous devez ajouter la propriété suivante dans votre fichier application.properties ou application.yml :


# Pour application.properties
logging.config=classpath:logback-spring.xml
logging.path=logs/application # Chemin de base pour les logs, sera utilisé par la propriété log.path dans le XML

# Pour application.yml
logging:
  config: classpath:logback-spring.xml
  path: logs/application

Avec cette configuration, les logs seront non seulement affichés dans la console mais aussi enregistrés dans des fichiers distincts pour chaque niveau (DEBUG, INFO, WARN, ERROR). Ces fichiers seront archivés quotidiennement et/ou lorsque leur taille dépasse 100 Mo, avec une conservation des 15 derniers jours. Le chemin et le nommage des fichiers sont définis dans le fichier logback-spring.xml.

Étiquettes: Spring Boot logback Gestion des logs Configuration journalisation

Publié le 17 octobre à 11h09