Dynamisches Ändern der log4j-Protokollstufe

127

Welche unterschiedlichen Ansätze gibt es, um die Protokollstufe log4j dynamisch zu ändern, damit ich die Anwendung nicht erneut bereitstellen muss? Werden die Änderungen in diesen Fällen dauerhaft sein?

Ravi
quelle
Sehr ähnlich zu stackoverflow.com/questions/2115900/…
vsingh
3
Aktualisierte Frage für log4j2: stackoverflow.com/questions/23434252/…
slaadvak
1
HINWEIS: (fast) alles auf dieser Seite handelt von log4j, nicht von log4j2. Diese ganze Seite ist so voller Verwirrung und Fehlleitung, dass sie USELESS ist. Gehen Sie zu stackoverflow.com/questions/23434252/…, wie von @slaadvak empfohlen.
Lambart

Antworten:

86

Das Ändern der Protokollstufe ist einfach. Das Ändern anderer Teile der Konfiguration bietet einen tieferen Ansatz.

LogManager.getRootLogger().setLevel(Level.DEBUG);

Die Veränderungen sind dauerhaft durch den Lebensstil des Logger. Bei der Neuinitialisierung wird die Konfiguration gelesen und verwendet, da durch das Festlegen des Pegels zur Laufzeit die Pegeländerung nicht beibehalten wird.

UPDATE: Wenn Sie Log4j 2 verwenden, sollten Sie die Aufrufe setLevelgemäß der Dokumentation entfernen, da dies über Implementierungsklassen erreicht werden kann.

Aufrufe von logger.setLevel () oder ähnlichen Methoden werden in der API nicht unterstützt. Anwendungen sollten diese entfernen. Gleichwertige Funktionen werden in den Log4j 2-Implementierungsklassen bereitgestellt, können jedoch dazu führen, dass die Anwendung Änderungen an den Log4j 2-Interna unterliegt.

Aaron McIver
quelle
5
Nur für LaufzeitabhängigkeitenLogManager.getLogger(Class.forName("org.hibernate.util.JDBCExceptionReporter")).setLevel(Level.FATAL);
CelinHC
8
Beachten Sie, dass die log4j 2-API keine "setLevel" -Methode bereitstellt.
ChrisCantrell
5
Aber das setzt nur den Root-Logger, nicht wahr? Wenn für Logger unter root individuelle Ebenen festgelegt sind, hat das Festlegen des Root-Loggers keine Auswirkungen auf diese LOGGER. Müssten wir nicht so etwas wie root.getLoggerRepository (). GetCurrentCategories () tun, über jede Instanz des Loggers iterieren und LEVELs für jeden Logger festlegen? @ AaronMcIver
TheMonkWhoSoldHisCode
8
@ChrisCantrell Log4j 2 bietet eine Möglichkeit, dies zu tun , obwohl es nicht so einfach ist.
CorayThan
2
Log4j2 kann so konfiguriert werden, dass seine Konfiguration aktualisiert wird, indem die Datei log4j2.xml (oder eine gleichwertige Datei) in bestimmten Intervallen gescannt wird. ZB <Configuration status = "warn" monitorInterval = "5" name = "tryItApp" packages = "">
Kimball Robinson
89

Datei Watchdog

Log4j kann die log4j.xmlDatei auf Konfigurationsänderungen überwachen . Wenn Sie die log4j-Datei ändern, aktualisiert log4j die Protokollstufen automatisch entsprechend Ihren Änderungen. org.apache.log4j.xml.DOMConfigurator.configureAndWatch(String,longEinzelheiten finden Sie in der Dokumentation von ). Die Standardwartezeit zwischen den Überprüfungen beträgt 60 Sekunden. Diese Änderungen bleiben bestehen, da Sie die Konfigurationsdatei direkt im Dateisystem ändern. Sie müssen lediglich DOMConfigurator.configureAndWatch () einmal aufrufen.

Achtung: Die configureAndWatch-Methode ist für die Verwendung in J2EE-Umgebungen aufgrund eines Thread-Lecks nicht sicher

JMX

Eine andere Möglichkeit, die Protokollstufe (oder die Neukonfiguration im Allgemeinen) log4j festzulegen, ist die Verwendung von JMX. Log4j registriert seine Logger als JMX-MBeans. Mit den MBeanServer-Konsolen der Anwendungsserver (oder der jconsole.exe von JDK) können Sie jeden einzelnen Logger neu konfigurieren. Diese Änderungen sind nicht dauerhaft und werden nach dem Neustart Ihrer Anwendung (Server) auf die in der Konfigurationsdatei festgelegte Konfiguration zurückgesetzt.

Selbstgemacht

Wie von Aaron beschrieben, können Sie die Protokollstufe programmgesteuert einstellen. Sie können es in Ihrer Anwendung so implementieren, wie Sie es möchten. Beispielsweise könnten Sie eine GUI haben, in der der Benutzer oder Administrator die Protokollstufe ändert und dann die setLevel()Methoden auf dem Protokollierer aufruft. Ob Sie die Einstellungen irgendwo beibehalten oder nicht, liegt bei Ihnen.

mhaller
quelle
8
Ein Wort zur Vorsicht in Bezug auf den log4j-Watchdog-Ansatz: "Da configureAndWatch einen separaten Watchdog-Thread startet und dieser Thread in log4j 1.2 nicht gestoppt werden kann, ist die configureAndWatch-Methode für die Verwendung in J2EE-Umgebungen, in denen Anwendungen recycelt werden, nicht sicher." Referenz: Log4J FAQ
Somu
Wenn ich die configureAndWatch-Funktionalität von Log4j verwenden würde, könnte ich diesen Watchdog-Thread in der @ PostDestroy-Methode eines EJB stoppen (das ist ein guter Indikator dafür, wann der Container heruntergefahren wird). Oder fehlt mir noch mehr ..!
Robin Bajaj
Entschuldigung, ich meinte @ PreDestroy Methode
Robin Bajaj
" Log4j registriert seine Logger als JMX-MBeans. " Mein Servlet verwendet log4j 1.2. Ich sehe keine log4j-MBeans.
Abdull
Sie müssen lediglich DOMConfigurator.configureAndWatch () einmal aufrufen. Wie kann ich das erreichen?
gstackoverflow
5

Log4j2 kann so konfiguriert werden, dass seine Konfiguration aktualisiert wird, indem die XML- Datei log4j 2 (oder eine gleichwertige Datei) in bestimmten Intervallen gescannt wird . Fügen Sie einfach den Parameter " monitorInterval " zu Ihrem Konfigurations-Tag hinzu. Siehe Zeile 2 der XML-Beispieldatei log4j 2 , in der log4j angewiesen wird, die Konfiguration erneut zu scannen, wenn seit dem letzten Protokollereignis mehr als 5 Sekunden vergangen sind.

<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="warn" monitorInterval="5" name="tryItApp" packages="">

    <Appenders>
        <RollingFile name="MY_TRY_IT"
                     fileName="/var/log/tryIt.log"
                     filePattern="/var/log/tryIt-%i.log.gz">
            <Policies>
                <SizeBasedTriggeringPolicy size="25 MB"/>
            </Policies>
            ...
        </RollingFile>
    </Appenders>


    <Loggers>
        <Root level="error">
            <AppenderRef ref="MY_TRY_IT"/>
        </Root>
    </Loggers>

</Configuration>

Es gibt zusätzliche Schritte, damit dies funktioniert, wenn Sie auf einer Tomcat-Instanz, in einer IDE oder bei Verwendung von Spring Boot bereitstellen. Das scheint hier etwas außerhalb des Rahmens zu liegen und verdient wahrscheinlich eine gesonderte Frage.

Kimball Robinson
quelle
@vsingh, stellen Sie in Spring Boot, Tomcat oder einem Container oder mit einer IDE bereit? In diesen Fällen kann es manchmal zu zusätzlichen Schritten kommen (nicht die Schuld von log4j2). Grundsätzlich kann die App nicht sehen, dass sich die Datei ändert, da sie von einem Framework oder Tool an einen anderen Speicherort kopiert wurde.
Kimball Robinson
Hallo, ich habe dies auf Jboss6.4 getestet und die Konfigurationsdatei log4j2 unter dem Speicherort (innerhalb der WAR-Datei) aktualisiert, an dem die App die Datei sehen kann. Es hat jedoch immer noch nicht funktioniert. Irgendein Vorschlag?
MidTierDeveloper
3

Diese Antwort hilft Ihnen nicht dabei, die Protokollierungsstufe dynamisch zu ändern. Sie müssen den Dienst neu starten. Wenn Sie den Dienst problemlos neu starten können, verwenden Sie bitte die folgende Lösung

Ich habe dies getan, um die Protokollstufe log4j zu ändern, und es hat bei mir funktioniert. Ich habe kein Dokument weitergeleitet. Ich habe diesen Systemeigenschaftswert verwendet, um meinen Protokolldateinamen festzulegen. Ich habe dieselbe Technik verwendet, um auch die Protokollierungsstufe festzulegen, und es hat funktioniert

hat dies als JVM-Parameter übergeben (ich verwende Java 1.7)

Dies ändert leider nicht dynamisch die Protokollierungsstufe, sondern erfordert einen Neustart des Dienstes

java -Dlogging.level=DEBUG -cp xxxxxx.jar  xxxxx.java

In der Datei log4j.properties habe ich diesen Eintrag hinzugefügt

log4j.rootLogger=${logging.level},file,stdout

Ich habe es versucht

 java -Dlogging.level=DEBUG -cp xxxxxx.jar  xxxxx.java
 java -Dlogging.level=INFO-cp xxxxxx.jar  xxxxx.java
 java -Dlogging.level=OFF -cp xxxxxx.jar  xxxxx.java

Es hat alles funktioniert. hoffe das hilft!

Ich habe diese folgenden Abhängigkeiten in meiner pom.xml

<dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>apache-log4j-extras</artifactId>
    <version>1.2.17</version>
</dependency>
Anandkumar
quelle
2
Was auch immer Sie erwähnt haben, scheint in Ordnung zu sein, aber die Frage betrifft die dynamische Änderung der Protokollierungsstufe.
Azim
Dazu müssen Sie den Dienst neu starten. Dadurch werden die Protokollierungsstufen nicht dynamisch geändert.
kk.
2

Mit log4j 1.x finde ich, dass der beste Weg darin besteht, einen DOMConfigurator zu verwenden, um eine vordefinierte Reihe von XML-Protokollkonfigurationen zu senden (z. B. eine für den normalen Gebrauch und eine für das Debuggen).

Die Nutzung dieser Möglichkeiten kann wie folgt erfolgen:

  public static void reconfigurePredefined(String newLoggerConfigName) {
    String name = newLoggerConfigName.toLowerCase();
    if ("default".equals(name)) {
      name = "log4j.xml";
    } else {
      name = "log4j-" + name + ".xml";
    }

    if (Log4jReconfigurator.class.getResource("/" + name) != null) {
      String logConfigPath = Log4jReconfigurator.class.getResource("/" + name).getPath();
      logger.warn("Using log4j configuration: " + logConfigPath);
      try (InputStream defaultIs = Log4jReconfigurator.class.getResourceAsStream("/" + name)) {
        new DOMConfigurator().doConfigure(defaultIs, LogManager.getLoggerRepository());
      } catch (IOException e) {
        logger.error("Failed to reconfigure log4j configuration, could not find file " + logConfigPath + " on the classpath", e);
      } catch (FactoryConfigurationError e) {
        logger.error("Failed to reconfigure log4j configuration, could not load file " + logConfigPath, e);
      }
    } else {
      logger.error("Could not find log4j configuration file " + name + ".xml on classpath");
    }
  }

Rufen Sie dies einfach mit dem entsprechenden Konfigurationsnamen auf und stellen Sie sicher, dass Sie die Vorlagen in den Klassenpfad einfügen.

ISparks
quelle
Was ist der Auslöser dieser Methode? Woher weiß es, dass diese Methode aufgerufen werden muss, wenn sich die log4j-Konfiguration ändert?
Asgs
Ich verstehe immer noch nicht, was Sie "auf Anfrage" meinen. Können Sie bitte einen Ausschnitt zeigen, in dem Sie diese Methode anschließen?
Asgs
2
Los geht's: Dies ist ein Ausschnitt aus dem Controller, in dem wir ihn verwenden. Wir haben eine Administrationsseite mit einigen Links zum dynamischen Neukonfigurieren der Protokollierung, die dann ein GET zum Link / admin / setLogLevel? Level = Error erstellt und dies dann im Controller wie folgt abfängt: @RequestMapping (value = "setLogLevel", method = RequestMethod.GET) public ModelAndView setLogLevel (@RequestParam (Wert = "Ebene", erforderlich = wahr) Zeichenfolgenebene) {Log4jReconfigurator.reconfigureExisting (Ebene);
ISparkes
"Auf Anfrage" bedeutet also "wenn der Benutzer auf die Schaltfläche klickt" in meinem Fall
ISparkes
Ah, hast du. Ihre Administratorkonsole muss also eine Anfrage stellen, um diese Methode aufzurufen.
Asgs
0

Ich habe diese Methode mit Erfolg verwendet, um die Ausführlichkeit der "org.apache.http" -Protokolle zu reduzieren:

ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("org.apache.http");
logger.setLevel(Level.TRACE);
logger.setAdditive(false);
Thomas Bernardin
quelle
0

Für die log4j 2-API können Sie verwenden

Logger logger = LogManager.getRootLogger();
Configurator.setAllLevels(logger.getName(), Level.getLevel(level));
mohamed stitane
quelle
-1

Wenn Sie die Protokollierungsstufe aller Protokollierer ändern möchten, verwenden Sie die folgende Methode. Dadurch werden alle Protokollierer aufgelistet und die Protokollierungsstufe auf die angegebene Stufe geändert. Bitte stellen Sie sicher , dass Sie NICHT SIE haben log4j.appender.loggerName.Threshold=DEBUGEigenschaftssatz in Ihrer log4j.propertiesDatei.

public static void changeLogLevel(Level level) {
    Enumeration<?> loggers = LogManager.getCurrentLoggers();
    while(loggers.hasMoreElements()) {
        Logger logger = (Logger) loggers.nextElement();
        logger.setLevel(level);
    }
}
kk.
quelle
-3

Sie können das folgende Code-Snippet verwenden

((ch.qos.logback.classic.Logger)LoggerFactory.getLogger(packageName)).setLevel(ch.qos.logback.classic.Level.toLevel(logLevel));
user2606570
quelle
Ich muss es versuchen.
user2045474