Frage Log-Level der Nachricht zur Laufzeit in slf4j setzen


Wenn Sie log4j verwenden, wird die Logger.log(Priority p, Object message) Diese Methode ist verfügbar und kann zum Protokollieren einer Nachricht auf einer zur Laufzeit bestimmten Protokollstufe verwendet werden. Wir benutzen diese Tatsache und dieser Tipp um stderr auf einer bestimmten Log-Ebene zu einem Logger umzuleiten.

slf4j hat kein generisches log() Methode, die ich finden kann. Bedeutet das, dass es keine Möglichkeit gibt, das Obige zu implementieren?


76
2018-04-12 11:39


Ursprung


Antworten:


Es gibt keine Möglichkeit, dies zu tun slf4j.

Ich stelle mir vor, dass der Grund, warum diese Funktionalität fehlt, dass es nahezu unmöglich ist, ein Konstrukt zu erstellen Level tippen für slf4j das kann effizient auf die abgebildet werden Level (oder gleichwertiger) Typ, der in allen möglichen Protokollierungsimplementierungen hinter der Fassade verwendet wird. Alternativ haben die Designer das entschieden Ihr Anwendungsfall ist zu ungewöhnlich um die Gemeinkosten zu rechtfertigen.

Über @ Ripper234ist es Anwendungsfall (Unit Testing), ich denke, die pragmatische Lösung ist die Modifizierung der Unit-Test (s) zu hard-wire Wissen, welches Protokollsystem ist hinter der slf4j Fassade ... beim Ausführen der Komponententests.


36
2018-04-12 12:36



Richard Fearn hat die richtige Idee, also habe ich die ganze Klasse basierend auf seinem Skelett-Code geschrieben. Es ist hoffentlich kurz genug, um hier zu posten. Kopieren & Einfügen zum Vergnügen. Ich sollte wahrscheinlich auch eine Zauberformel hinzufügen: "Dieser Code wird veröffentlicht"

import org.slf4j.Logger;

public class LogLevel {

    /**
     * Allowed levels, as an enum. Import using "import [package].LogLevel.Level"
     * Every logging implementation has something like this except SLF4J.
     */

    public static enum Level {
        TRACE, DEBUG, INFO, WARN, ERROR
    }

    /**
     * This class cannot be instantiated, why would you want to?
     */

    private LogLevel() {
        // Unreachable
    }

    /**
     * Log at the specified level. If the "logger" is null, nothing is logged.
     * If the "level" is null, nothing is logged. If the "txt" is null,
     * behaviour depends on the SLF4J implementation.
     */

    public static void log(Logger logger, Level level, String txt) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(txt);
                break;
            case DEBUG:
                logger.debug(txt);
                break;
            case INFO:
                logger.info(txt);
                break;
            case WARN:
                logger.warn(txt);
                break;
            case ERROR:
                logger.error(txt);
                break;
            }
        }
    }

    /**
     * Log at the specified level. If the "logger" is null, nothing is logged.
     * If the "level" is null, nothing is logged. If the "format" or the "argArray"
     * are null, behaviour depends on the SLF4J-backing implementation.
     */

    public static void log(Logger logger, Level level, String format, Object[] argArray) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(format, argArray);
                break;
            case DEBUG:
                logger.debug(format, argArray);
                break;
            case INFO:
                logger.info(format, argArray);
                break;
            case WARN:
                logger.warn(format, argArray);
                break;
            case ERROR:
                logger.error(format, argArray);
                break;
            }
        }
    }

    /**
     * Log at the specified level, with a Throwable on top. If the "logger" is null,
     * nothing is logged. If the "level" is null, nothing is logged. If the "format" or
     * the "argArray" or the "throwable" are null, behaviour depends on the SLF4J-backing
     * implementation.
     */

    public static void log(Logger logger, Level level, String txt, Throwable throwable) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(txt, throwable);
                break;
            case DEBUG:
                logger.debug(txt, throwable);
                break;
            case INFO:
                logger.info(txt, throwable);
                break;
            case WARN:
                logger.warn(txt, throwable);
                break;
            case ERROR:
                logger.error(txt, throwable);
                break;
            }
        }
    }

    /**
     * Check whether a SLF4J logger is enabled for a certain loglevel. 
     * If the "logger" or the "level" is null, false is returned.
     */

    public static boolean isEnabledFor(Logger logger, Level level) {
        boolean res = false;
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                res = logger.isTraceEnabled();
                break;
            case DEBUG:
                res = logger.isDebugEnabled();
                break;
            case INFO:
                res = logger.isInfoEnabled();
                break;
            case WARN:
                res = logger.isWarnEnabled();
                break;
            case ERROR:
                res = logger.isErrorEnabled();
                break;
            }
        }
        return res;
    }
}

21
2017-10-19 23:41



Sie können dies mit Java 8 Lambdas implementieren.

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;

public class LevelLogger {
    private static final Logger LOGGER = LoggerFactory.getLogger(LevelLogger.class);
    private static final Map<Level, LoggingFunction> map;

    static {
        map = new HashMap<>();
        map.put(Level.TRACE, (o) -> LOGGER.trace(o));
        map.put(Level.DEBUG, (o) -> LOGGER.debug(o));
        map.put(Level.INFO, (o) -> LOGGER.info(o));
        map.put(Level.WARN, (o) -> LOGGER.warn(o));
        map.put(Level.ERROR, (o) -> LOGGER.error(o));
    }

    public static void log(Level level, String s) {
        map.get(level).log(s);
    }

    @FunctionalInterface
    private interface LoggingFunction {
        public void log(String arg);
    }
}

9
2018-03-10 17:01



Versuchen Sie, zu Logback zu wechseln und zu verwenden

ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.toLevel("info"));

Ich glaube, dies wird der einzige Aufruf von Logback sein und der Rest Ihres Codes wird unverändert bleiben. Logback verwendet SLF4J und die Migration wird schmerzlos sein, nur die XML-Konfigurationsdateien müssen geändert werden.

Denken Sie daran, die Protokollstufe zurückzusetzen, nachdem Sie fertig sind.


9
2018-01-18 13:20



Dies kann mit einem durchgeführt werden enum und eine Hilfsmethode:

enum LogLevel {
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
}

public static void log(Logger logger, LogLevel level, String format, Object[] argArray) {
    switch (level) {
        case TRACE:
            logger.trace(format, argArray);
            break;
        case DEBUG:
            logger.debug(format, argArray);
            break;
        case INFO:
            logger.info(format, argArray);
            break;
        case WARN:
            logger.warn(format, argArray);
            break;
        case ERROR:
            logger.error(format, argArray);
            break;
    }
}

// example usage:
private static final Logger logger = ...
final LogLevel level = ...
log(logger, level, "Something bad happened", ...);

Sie könnten weitere Varianten von hinzufügen log, sagen Sie, wenn Sie generische Äquivalente von SLF4J 1-Parameter oder 2-Parameter wollten warn/error/etc. Methoden.


6
2018-05-25 21:46



Jeder, der eine vollständig SLF4J-kompatible Lösung für dieses Problem sucht, möchte vielleicht auschecken Lidalia SLF4J Erweiterungen - Es ist auf Maven Central.


4
2018-03-28 13:14



Ich bin gerade auf ein ähnliches Bedürfnis gestoßen. In meinem Fall ist slf4j mit dem Java Logging Adapter (der jdk14 one) konfiguriert. Mit dem folgenden Code-Snippet habe ich es geschafft, die Debug-Ebene zur Laufzeit zu ändern:

Logger logger = LoggerFactory.getLogger("testing");
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger("testing");
julLogger.setLevel(java.util.logging.Level.FINE);
logger.debug("hello world");

1
2018-06-11 03:33



Basierend auf der Antwort von massimo virgilio, habe ich es auch mit slf4j-log4j mit Introspektion geschafft. HTH.

Logger LOG = LoggerFactory.getLogger(MyOwnClass.class);

org.apache.logging.slf4j.Log4jLogger LOGGER = (org.apache.logging.slf4j.Log4jLogger) LOG;

try {
    Class loggerIntrospected = LOGGER.getClass();
    Field fields[] = loggerIntrospected.getDeclaredFields();
    for (int i = 0; i < fields.length; i++) {
        String fieldName = fields[i].getName();
        if (fieldName.equals("logger")) {
            fields[i].setAccessible(true);
            org.apache.logging.log4j.core.Logger loggerImpl = (org.apache.logging.log4j.core.Logger) fields[i].get(LOGGER);
            loggerImpl.setLevel(Level.DEBUG);
        }
    }
} catch (Exception e) {
    System.out.println("ERROR :" + e.getMessage());
}

0
2018-02-24 12:39



Hier ist eine Lambda-Lösung, die nicht so benutzerfreundlich ist wie @Paul Croarkin in einer Hinsicht (der Level wird effektiv zweimal vergeben). Aber ich denke (a) der Benutzer sollte den Logger passieren; und (B) AFAIU die ursprüngliche Frage nicht für eine bequeme Möglichkeit für überall in der Anwendung, nur eine Situation mit wenigen Verwendungen in einer Bibliothek.

package test.lambda;
import java.util.function.*;
import org.slf4j.*;

public class LoggerLambda {
    private static final Logger LOG = LoggerFactory.getLogger(LoggerLambda.class);

    private LoggerLambda() {}

    public static void log(BiConsumer<? super String, ? super Object[]> logFunc, Supplier<Boolean> logEnabledPredicate, 
            String format, Object... args) {
        if (logEnabledPredicate.get()) {
            logFunc.accept(format, args);
        }
    }

    public static void main(String[] args) {
        int a = 1, b = 2, c = 3;
        Throwable e = new Exception("something went wrong", new IllegalArgumentException());
        log(LOG::info, LOG::isInfoEnabled, "a = {}, b = {}, c = {}", a, b, c);

        // warn(String, Object...) instead of warn(String, Throwable), but prints stacktrace nevertheless
        log(LOG::warn, LOG::isWarnEnabled, "error doing something: {}", e, e);
    }
}

Seit slf4j erlaubt Throwable (deren Stack-Trace protokolliert werden soll) im Varargs-ParameterIch denke, es besteht keine Notwendigkeit, das zu überlasten logHilfsmethode für andere Verbraucher als (String, Object[]).


0
2017-07-29 09:30