web-dev-qa-db-fra.com

logback: deux appenders, plusieurs loggers, différents niveaux

Je souhaite avoir deux fichiers journaux dans mon application (intégration Spring), debug.log et main.log. Je veux exécuter main.log au niveau INFO et debug.log au niveau DEBUG. Ceci est faisable avec des filtres sur les annexes. Je veux connecter différents niveaux aux appenders en fonction de la source. En d'autres termes

<logger name="org.springframework" level="ERROR">
    <appender-ref ref="main" />
</logger>
<logger name="org.springframework" level="DEBUG">
    <appender-ref ref="debug" />
</logger>
<logger name="com.myapp" level="INFO">
    <appender-ref ref="main" />
</logger>
<logger name="com.myapp" level="DEBUG">
    <appender-ref ref="debug" />
</logger>

Donc, pour résumer:

  1. Logger de printemps
    • main -> ERREUR
    • debug -> DEBUG
  2. enregistreur com.myapp
    • main -> INFO
    • debug -> DEBUG

À cause de cela, je dois avoir les enregistreurs fonctionnant sur DEBUG et un filtre de seuil sur un appender n'est pas assez précis.

Mise à jour Ajout de clarté à la question

47
John Oxley

Créez une classe ThresholdLoggerFilter pouvant être placée sur un appender comme:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>
    <filter class="com.myapp.ThresholdLoggerFilter">
        <logger>org.springframework</logger>
        <level>ERROR</level>
    </filter>
    </appender>

Le code suivant fonctionne

package com.myapp;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;

public class ThresholdLoggerFilter extends Filter<ILoggingEvent> {
    private Level level;
    private String logger;

    @Override
    public FilterReply decide(ILoggingEvent event) {
        if (!isStarted()) {
            return FilterReply.NEUTRAL;
        }

        if (!event.getLoggerName().startsWith(logger))
            return FilterReply.NEUTRAL;

        if (event.getLevel().isGreaterOrEqual(level)) {
            return FilterReply.NEUTRAL;
        } else {
            return FilterReply.DENY;
        }
    }

    public void setLevel(Level level) {
        this.level = level;
    }

    public void setLogger(String logger) {
        this.logger = logger;
    }

    public void start() {
        if (this.level != null && this.logger != null) {
            super.start();
        }
    }
}
49
John Oxley

Vous pouvez également le faire un peu plus simplement si vous souhaitez hériter de l'enregistreur racine, par exemple. nous ajoutons ici un enregistreur supplémentaire pour les erreurs, qui se connecte à stderr. Il n'est activé que pour certains enregistreurs.

<configuration>
    <appender name="CONSOLE-stdout" class="ch.qos.logback.core.ConsoleAppender">
        <target>System.out</target>
        <encoder>
            <pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="CONSOLE-stderr" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>

        <target>System.err</target>
        <encoder>
            <pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="CONSOLE-stdout" />
    </root>

        <!-- We want error logging from this logger to go to an extra appender 
             It still inherits CONSOLE-stdout from the root logger -->
    <logger name="org.springframework" level="INFO">
        <appender-ref ref="CONSOLE-stderr" />
    </logger>
</configuration>
15
artbristol

Ajouter une solution supplémentaire plus simple que ce qui existe déjà

Aucune de ces solutions n'a fonctionné pour moi, car je n'utilise pas de framework comme Spark ou Spring. J'ai donc fait quelque chose d'un peu plus simple qui semble bien fonctionner. Bien que cette solution puisse ne pas fonctionner pour le PO, elle peut être utile à quelqu'un qui veut quelque chose de moins encombrant.

<property name="pattern" value="%d{yyyy.MMM.dd HH:mm:ss.SSS} [ProgramName] %level - %msg%n" />

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>/path/to/your/program.log</file>
    <append>true</append>
    <encoder>
        <pattern>${pattern}</pattern>
    </encoder>
</appender>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <target>System.out</target>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>
    <encoder>
        <pattern>${pattern}</pattern>
    </encoder>
</appender>

<root level="debug">
    <appender-ref ref="FILE" />
    <appender-ref ref="STDOUT" />
</root>

Avec cette configuration, je peux garder la console plutôt propre, tout en exportant les instructions DEBUG dans le fichier journal.

3
Jonathan Landrum

Je viens de trouver une solution pratique utilisant uniquement des éléments de journalisation et fonctionnant plutôt bien. En principe, vous devez avoir deux ajouts, l'un avec la configuration par défaut et l'autre avec un filtre (dans mon exemple, j'utilise la console):

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="WARN_FILTER_STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="org.Apache.spark" level="INFO" additivity="false">
        <appender-ref ref="SPARK" /><!-- this line is not necessary, just here to ilustrate the need for the filter -->
        <appender-ref ref="WARN_FILTER_STDOUT" />
    </logger>

    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
2
eduardohl

Utiliser plusieurs enregistreurs pour différents messages ressemblera à ceci:

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext; 
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;

public class ConfigureLogBack 
{
    public static void programmaticConfiguration()
    {
        Logger camel = getLogger("MyRoute", C:\\Users\\amrut.malaji\\Desktop\\Oracle\\logback\\camel-Log.txt");
        Logger services = getLogger("webservices", "C:\\Users\\amrut.malaji\\Desktop\\Oracle\\logback\\services-log.txt");
    }

    private static Logger getLogger(String string, String file) {
        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    PatternLayoutEncoder ple = new PatternLayoutEncoder();

    ple.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
    ple.setContext(lc);
    ple.start();
    FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
    fileAppender.setFile(file);
    fileAppender.setEncoder(ple);
    fileAppender.setContext(lc);
    fileAppender.start();

    Logger logger = (Logger) LoggerFactory.getLogger(string);
    logger.addAppender(fileAppender);
    logger.setLevel(Level.INFO);
    logger.setAdditive(false); /* set to true if root should log too */

    return logger;
}
0
Amrut Malaji

un filtre de seuil sur un appender n'est pas assez fin

Vous pouvez utiliser un EvaluatorFilter. JaninoEventEvaluator aurait besoin d'une référence à janino (.jar) et un exemple de logback.xml serait:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">      
        <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
            <expression>
                level &lt;= ERROR &amp;&amp; logger.equals(&quot;com.myapp.ThresholdLoggerFilter&quot;)
            </expression>
        </evaluator>
        <OnMismatch>DENY</OnMismatch>
        <OnMatch>NEUTRAL</OnMatch>
    </filter>
 </appender>

Cette approche utilise une expression Java dans la balise expression (doit être un échappement xml) pour évaluer l'événement de journalisation. Aucune classe Java personnalisée n'est nécessaire pour l'écriture.

0
beigemartin