Konfigurieren Sie den LogBack-Appender programmgesteuert

74

Ich habe einen Logback-Appender in der Datei logback.xml definiert, es ist ein DB-Appender, aber ich bin gespannt, ob es eine Möglichkeit gibt, den Appender in Java mithilfe meines eigenen Verbindungspools zu konfigurieren, der als Bean definiert ist.

Ich finde ähnliche Dinge, aber niemals die eigentliche Antwort.


quelle

Antworten:

113

Hier ein einfaches Beispiel, das für mich funktioniert (beachten Sie, dass ich in diesem Beispiel den FileAppender verwende)

import org.slf4j.LoggerFactory;

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 Loggerutils {

    public static void main(String[] args) {
          Logger foo = createLoggerFor("foo", "foo.log");
          Logger bar = createLoggerFor("bar", "bar.log");
          foo.info("test");
          bar.info("bar");
    }

    private static Logger createLoggerFor(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.DEBUG);
          logger.setAdditive(false); /* set to true if root should log too */

          return logger;
    }

}
reto
quelle
@ Andrew Swan: Könnten Sie bitte in einem Kommentar vermerken, was Sie mit der aktuellen Version ändern möchten. Ich werde versuchen, es entsprechend zu aktualisieren.
Reto
7
Loggermacht die addAppender()Methode nicht mehr verfügbar .
Mike C
1
@revor Ich erinnere mich nicht und diese Java-Codebasis ist jetzt unerreichbar.
Mike C
3
Für diejenigen, die erkennen, Loggerdass die addAppenderMethode nicht vorhanden ist, liegt es daran, dass Sie auf die Abstraktionsklasse Loggervon sl4j hinweisen. Wenn Sie auf die Implementierung Loggeraus dem logbackPaket hinweisen, können Sie die verwenden addAppender. Der Nachteil dabei ist, dass Ihr Testcode an die logbackImplementierung gekoppelt wird .
Tiago Zortéa De Conto
1
besser zu bedienen lc.getLogger(string).
user8681709
14

Sie können Appender programmgesteuert konfigurieren. Fast alle Appender werden mit programmatischer Konfiguration getestet. Daraus folgt, dass der Quellcode des Logback-Projekts viele Beispiele für die Konfiguration programmatischer Appender enthält. Suchen Sie für einen Logback-Core-Appender unter logback-core/src/test/javaund für einen Logback-klassischen Appender unter logback-classic/src/test/java.

Ceki
quelle
10

Wenn Sie versuchen, Code zu ändern, der für die Erstellung von Loggern verantwortlich ist, müssen als Referenz eine Reihe von Regeln erfüllt sein, damit ein Logger funktioniert.

Diese Regeln wurden in einem großartigen und hilfreichen Artikel Programmatische Konfiguration von slf4j / logback beschrieben :

Jetzt habe ich Erfahrung mit der programmatischen Konfiguration von slf4j / logback.

Aufgabe

Ein Programm muss für jede verarbeitete Eingabedatei eine separate Protokolldatei öffnen.

Lösung für Aufgabe

Anstatt Logback über XML zu konfigurieren, muss man Encoder, Appender und Logger „manuell“ instanziieren und sie dann konfigurieren und miteinander verknüpfen.

Vorsichtsmaßnahme 1

Logback wird verrückt, wenn versucht wird, den Encoder (dh PatternLayoutEncoder) zwischen Appendern zu teilen.

Lösung für Vorbehalt 1

Erstellen Sie für jeden Appender einen separaten Encoder.

Vorsichtsmaßnahme 2

Logback weigert sich, irgendetwas zu protokollieren, wenn Encoder und Appender nicht mit dem Protokollierungskontext verknüpft sind.

Lösung für Vorbehalt 2

Rufen Sie setContext für jeden Encoder und Appender auf und übergeben Sie LoggerFactory als Parameter.

Vorsichtsmaßnahme 3

Logback weigert sich, irgendetwas zu protokollieren, wenn Encoder und Appender nicht gestartet werden.

Lösung für Vorbehalt 3

Encoder und Appender müssen in der richtigen Reihenfolge gestartet werden, dh zuerst Encoder, dann Appender.

Vorsichtsmaßnahme 4

RollingPolicy-Objekte (dh TimeBasedRollingPolicy) erzeugen seltsame Fehlermeldungen wie "Datumsformat nicht erkannt", wenn sie nicht an denselben Kontext wie Appender angehängt sind.

Lösung für Vorbehalt 4

Rufen Sie setContext auf RollingPolicy genauso auf wie auf Encodern und Appendern.

Hier ist ein Arbeitsbeispiel für die manuelle Logback-Konfiguration:

package testpackage

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.core.ConsoleAppender
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy

import org.slf4j.LoggerFactory

class TestLogConfig {

  public static void main(String[] args) {
    LoggerContext logCtx = LoggerFactory.getILoggerFactory();

    PatternLayoutEncoder logEncoder = new PatternLayoutEncoder();
    logEncoder.setContext(logCtx);
    logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
    logEncoder.start();

    ConsoleAppender logConsoleAppender = new ConsoleAppender();
    logConsoleAppender.setContext(logCtx);
    logConsoleAppender.setName("console");
    logConsoleAppender.setEncoder(logEncoder);
    logConsoleAppender.start();

    logEncoder = new PatternLayoutEncoder();
    logEncoder.setContext(logCtx);
    logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n");
    logEncoder.start();

    RollingFileAppender logFileAppender = new RollingFileAppender();
    logFileAppender.setContext(logCtx);
    logFileAppender.setName("logFile");
    logFileAppender.setEncoder(logEncoder);
    logFileAppender.setAppend(true);
    logFileAppender.setFile("logs/logfile.log");

    TimeBasedRollingPolicy logFilePolicy = new TimeBasedRollingPolicy();
    logFilePolicy.setContext(logCtx);
    logFilePolicy.setParent(logFileAppender);
    logFilePolicy.setFileNamePattern("logs/logfile-%d{yyyy-MM-dd_HH}.log");
    logFilePolicy.setMaxHistory(7);
    logFilePolicy.start();

    logFileAppender.setRollingPolicy(logFilePolicy);
    logFileAppender.start();

    Logger log = logCtx.getLogger("Main");
    log.setAdditive(false);
    log.setLevel(Level.INFO);
    log.addAppender(logConsoleAppender);
    log.addAppender(logFileAppender);
  }
}
dominik
quelle
1
Aus irgendeinem Grund bekomme ichjava.lang.ClassCastException: org.apache.logging.slf4j.Log4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
Sridhar Sarnobat
Es scheint, dass Sie die Logger-Factory von Log4j verwenden, nicht slf4j
xxSwordy
"Main"Name scheint ungültig zu sein. Logger.ROOT_LOGGER_NAMEist "ROOT".
Miha_x64
@ Miha_x64: Jede Zeichenfolge ist gültig. Das Übergeben von "ROOT" würde das Konfigurieren der Stamminstanz des Loggers bedeuten. Was ist, wenn Sie mehr Logger konfigurieren möchten, die vom Root-Logger erben? Sie erstellen untergeordnete Logger (z. B. "Main", "Main2", "Auxiliary", "WhateverNameYouWant") und alle diese untergeordneten Logger können benutzerdefiniert konfiguriert werden. Siehe Javadoc für: slf4j.org/api/org/slf4j/…
dominik
6

Nur wenn jemand nach einem konkreten Beispiel für eine programmatische Konfiguration suchen würde.

Hier richte ich den Zeichensatz von ConsoleAppender ein:

LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
ConsoleAppender<ILoggingEvent> appender =
    (ConsoleAppender) lc.getLogger("appconsole").getAppender("STDOUT");
LayoutWrappingEncoder<ILoggingEvent> enc = 
    (LayoutWrappingEncoder<ILoggingEvent>) appender.getEncoder();
enc.setCharset(Charset.forName("utf-8"));

Und meine logback.xml:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <charset>866</charset>
        <pattern>[%level] %msg%n</pattern>
    </encoder>
</appender>

<logger name="appconsole">
    <appender-ref ref="STDOUT" />
</logger>

Warum muss ich programmgesteuert einen Logger konfigurieren? Weil ich meine App (Spring Boot) in eine JAR-Datei packe. Folglich scheint die Datei Logback.xml in einem Glas versteckt zu sein. Es ist jedoch nicht bequem, es auszupacken und zu ändern. Und ich brauche keine logback.xml-Datei neben meiner app.jar. Ich habe nur die Datei app.yaml, die alle Konfigurationseigenschaften für die App enthält.

Yan Pak
quelle
0

(Noch?) Nicht kommentieren dürfen, ich möchte nur drei Tipps hinzufügen;

  • Wenn Sie Probleme haben, fügen Sie einfach einen Anruf hinzu

    StatusPrinter.print(context);
    

    Nachdem alles konfiguriert wurde, dh nachdem Sie Ihre Appender hinzugefügt haben, wird Ihnen der Root / "Main" -Anhänger angezeigt, der Ihnen sagt, was falsch ist.

  • Ich mag es sehr, Protokollierungsstufen in verschiedenen Dateien zu trennen. Wenn ich nach Fehlern suche, beginne ich mit dem Suchen in der Fehlerdatei und so weiter

tot_[app name].log   : Level.INFO
deb_[app name].log   : Level.DEBUG
err_[app name].log   : Level.ERROR

Routing mittels einer einfachen privaten Filterklasse wie z

    private static class ThresholdLoggerFilter extends Filter<ILoggingEvent> {

        private final Level level;

        private ThresholdLoggerFilter(Level level){
            this.level = level;
        }

        @Override
        public FilterReply decide(ILoggingEvent event) {
            if (event.getLevel().isGreaterOrEqual(level)) {
                return FilterReply.NEUTRAL;
            } else {
                return FilterReply.DENY;
            }
        }
    }

und dann einfach anrufen myFilter.start()und myAppender.addFilter(myFilter);.

  • Wenn ich es zusammenstelle, möchte ich normalerweise in der Lage sein, die Protokollebenen dynamisch zu ändern, indem das Setup eine einfache Schnittstelle wie implementiert

    public interface LoggingService {
        void setRootLogLevel(Level level);
    }
    

Halten Sie die Root-Protokollierungsstufe in einer Eigenschaftendatei, die überwacht wird, so dass ich diesen Dienst einfach als implementiert bezeichne, wenn dort eine gültige Eingabe vorliegt

    @Override
    public void setRootLogLevel(Level level) {
        if (context != null && context.isStarted()) {
        ((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level);
        }
    }

mit meinem neuen Root-Logger-Level.

Ola Aronsson
quelle