Programmgesteuertes Konfigurieren von Log4j-Loggern

189

Ich versuche zum log4jersten Mal SLF4J (mit Bindung) zu verwenden.

Ich möchte 3 verschiedene Logger mit Namen konfigurieren, die von einer LoggerFactory zurückgegeben werden können, die verschiedene Ebenen protokolliert und die Nachrichten an verschiedene Appender weiterleitet:

  • Logger 1 "FileLogger" protokolliert DEBUG und hängt an DailyRollingFileAppender
  • Logger 2 "TracingLogger" protokolliert TRACE + und hängt an a JmsAppender
  • Logger 3 "ErrorLogger" protokolliert ERROR + und hängt an einen anderen an JmsAppender

Außerdem möchte ich, dass sie programmgesteuert konfiguriert werden (in Java im Gegensatz zu XML oder einer log4j.propertiesDatei).

Ich stelle mir vor, dass ich diese Loggers normalerweise irgendwo in einem Bootstrapping-Code definieren würde , wie bei einer init()Methode. Da ich jedoch verwenden möchte slf4j-log4j, bin ich verwirrt darüber, wo ich Logger definieren und sie dem Klassenpfad zur Verfügung stellen könnte.

Ich glaube nicht, dass dies eine Verletzung des zugrunde liegenden Zwecks von SLF4J (als Fassade) ist, da mein Code, der die SLF4J-API verwendet, niemals wissen wird, dass diese Logger existieren. Mein Code ruft nur normal die SLF4J-API auf, die sie dann an die log4j-Logger weiterleitet, die er im Klassenpfad findet.

Aber wie konfiguriere ich diese log4j-Logger im Klassenpfad ... in Java?!

IAmYourFaja
quelle
3
Für log4j 1.x verwenden Sie die akzeptierte Antwort unten für 2.x siehe logging.apache.org/log4j/2.x/manual/customconfig.html
earcam

Antworten:

277

Sie können Appender programmgesteuert zu Log4j hinzufügen / entfernen:

  ConsoleAppender console = new ConsoleAppender(); //create appender
  //configure the appender
  String PATTERN = "%d [%p|%c|%C{1}] %m%n";
  console.setLayout(new PatternLayout(PATTERN)); 
  console.setThreshold(Level.FATAL);
  console.activateOptions();
  //add appender to any Logger (here is root)
  Logger.getRootLogger().addAppender(console);

  FileAppender fa = new FileAppender();
  fa.setName("FileLogger");
  fa.setFile("mylog.log");
  fa.setLayout(new PatternLayout("%d %-5p [%c{1}] %m%n"));
  fa.setThreshold(Level.DEBUG);
  fa.setAppend(true);
  fa.activateOptions();

  //add appender to any Logger (here is root)
  Logger.getRootLogger().addAppender(fa);
  //repeat with all other desired appenders

Ich würde vorschlagen, dass Sie es irgendwo in ein init () einfügen, wo Sie sicher sind, dass dies vor allem anderen ausgeführt wird. Sie können dann alle vorhandenen Appender im Root-Logger mit entfernen

 Logger.getRootLogger().getLoggerRepository().resetConfiguration();

und beginnen Sie mit dem Hinzufügen Ihrer eigenen. Sie benötigen natürlich log4j im Klassenpfad, damit dies funktioniert.

Anmerkung:
Sie können beliebige Logger.getLogger(...)Appender hinzufügen. Ich habe gerade den Root-Logger genommen, weil er am Ende aller Dinge steht und alles verarbeitet, was durch andere Appender in anderen Kategorien geleitet wird (sofern nicht anders konfiguriert, indem das Additivitätsflag gesetzt wird).

Wenn Sie wissen möchten, wie die Protokollierung funktioniert und wie entschieden wird, wo Protokolle geschrieben werden, lesen Sie dieses Handbuch um weitere Informationen dazu zu erhalten.
Zusamenfassend:

  Logger fizz = LoggerFactory.getLogger("com.fizz")

erhalten Sie einen Logger für die Kategorie "com.fizz".
Für das obige Beispiel bedeutet dies, dass alles, was damit protokolliert wird, an die Konsole und den Dateianhänger im Root-Logger weitergeleitet wird.
Wenn Sie einen Appender zu Logger.getLogger ("com.fizz"). AddAppender (newAppender) hinzufügen, wird die Protokollierung fizzvon allen Appendern aus dem Root-Logger und dem ausgeführt newAppender.
Sie erstellen keine Logger mit der Konfiguration, sondern stellen nur Handler für alle möglichen Kategorien in Ihrem System bereit.

oers
quelle
2
Danke oers! Kurze Frage - Ich habe festgestellt, dass Sie die Appender zum Root-Logger hinzufügen. Gibt es einen Grund dafür?
IAmYourFaja
Und was noch wichtiger ist, ich muss angeben, welcher Logger aus der LoggerFactory von SLF4J abgerufen werden soll. Ist es möglich, SLF4J nach dem Root-Logger von log4j zu fragen?
IAmYourFaja
3
@AdamTannon Du kannst jeden Logger.getLogger (...) nehmen, den du magst. Ich habe gerade den Root-Logger genommen, weil er am Ende aller Dinge steht und alles handhabt, was durch andere Appender in anderen Kategorien geleitet wird (sofern nicht anders konfiguriert). Siehe Logger-Hierarchie
oers
@AdamTannon Sie können die sl4j-Factory nicht verwenden, um den log4j-Root-Logger abzurufen. SL4j ist eine Holzfassade. Sie erhalten nichts log4j-spezifisches davon.
Oers
2
oers - Ich freue mich über Ihr wunderbares Feedback, verbinde aber hier nicht alle Punkte. Können Sie Ihr Beispiel so ändern, dass ein neuer Logger (nicht der Root-Logger) hinzugefügt wird, der nach dem Hinzufügen zum System für jede andere Klasse verfügbar ist, die danach fragt? Zum Beispiel ein Logger, auf den normalerweise zugegriffen wird, z. B. Logger fizz = LoggerFactory.getLogger("com.fizz");Danke!
IAmYourFaja
47

Es hört sich so an, als würden Sie versuchen, log4j von "beiden Seiten" (dem Consumer-Ende und dem Konfigurations-Ende) zu verwenden.

Wenn Sie gegen die slf4j-API codieren möchten, aber die Konfiguration der log4j-Logger, die der Klassenpfad zurückgibt, im Voraus (und programmgesteuert) festlegen möchten, müssen Sie unbedingt eine Protokollierungsanpassung haben, die eine verzögerte Konstruktion verwendet.

public class YourLoggingWrapper {
    private static boolean loggingIsInitialized = false;

    public YourLoggingWrapper() {
        // ...blah
    }

    public static void debug(String debugMsg) {
        log(LogLevel.Debug, debugMsg);
    }

    // Same for all other log levels your want to handle.
    // You mentioned TRACE and ERROR.

    private static void log(LogLevel level, String logMsg) {
        if(!loggingIsInitialized)
            initLogging();

        org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger("DebugLogger");

        switch(level) {
        case: Debug:
            logger.debug(logMsg);
            break;
        default:
            // whatever
        }
    }

    // log4j logging is lazily constructed; it gets initialized
    // the first time the invoking app calls a log method
    private static void initLogging() {
        loggingIsInitialized = true;

        org.apache.log4j.Logger debugLogger = org.apache.log4j.LoggerFactory.getLogger("DebugLogger");

        // Now all the same configuration code that @oers suggested applies...
        // configure the logger, configure and add its appenders, etc.
        debugLogger.addAppender(someConfiguredFileAppender);
    }

Mit diesem Ansatz müssen Sie sich keine Gedanken darüber machen, wo / wann Ihre log4j-Logger konfiguriert werden. Wenn der Klassenpfad zum ersten Mal nach ihnen fragt, werden sie träge konstruiert, zurückgegeben und über slf4j verfügbar gemacht. Hoffe das hat geholfen!

IAmYourFaja
quelle
2
Geschafft! Vielen Dank für ein hilfreiches Beispiel! @Oers - danke, dass Sie versucht haben, mich in die richtige Richtung zu lenken - ich werde Ihnen den grünen Scheck für Ihr Engagement geben, muss aber zharvey das Kopfgeld geben, weil es genau das war, wonach ich gesucht habe. Nochmals vielen Dank an alle!
IAmYourFaja
4

Wenn Sie einen Appender in den log4j-Eigenschaften definiert haben und ihn programmgesteuert aktualisieren möchten, legen Sie den Namen in den log4j-Eigenschaften fest und rufen Sie ihn nach Namen ab.

Hier ist ein Beispiel für den Eintrag log4j.properties:

log4j.appender.stdout.Name=console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.Threshold=INFO

Gehen Sie wie folgt vor, um es zu aktualisieren:

((ConsoleAppender) Logger.getRootLogger().getAppender("console")).setThreshold(Level.DEBUG);
Kyle Shrader
quelle
1

Wenn jemand nach einer programmgesteuerten Konfiguration von log4j2 in Java sucht, kann dieser Link hilfreich sein: ( https://www.studytonight.com/post/log4j2-programmatic-configuration-in-java-class )

Hier ist der Basiscode zum Konfigurieren eines Console Appender:

ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();

builder.setStatusLevel(Level.DEBUG);
// naming the logger configuration
builder.setConfigurationName("DefaultLogger");

// create a console appender
AppenderComponentBuilder appenderBuilder = builder.newAppender("Console", "CONSOLE")
                .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
// add a layout like pattern, json etc
appenderBuilder.add(builder.newLayout("PatternLayout")
                .addAttribute("pattern", "%d %p %c [%t] %m%n"));
RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.DEBUG);
rootLogger.add(builder.newAppenderRef("Console"));

builder.add(appenderBuilder);
builder.add(rootLogger);
Configurator.reconfigure(builder.build());

Dadurch wird der Standard- RootLogger neu konfiguriert und ein neuer Appender erstellt .

iamabhishek
quelle