Pregunta Configuración del nivel de registro del mensaje en tiempo de ejecución en slf4j


Cuando se usa log4j, el Logger.log(Priority p, Object message) El método está disponible y se puede usar para registrar un mensaje en un nivel de registro determinado en tiempo de ejecución. Estamos usando este hecho y este consejo redireccionar stderr a un registrador en un nivel de registro específico.

slf4j no tiene un genérico log() método que puedo encontrar. ¿Eso significa que no hay forma de implementar lo anterior?


76
2018-04-12 11:39


origen


Respuestas:


No hay forma de hacer esto con slf4j.

Me imagino que la razón por la que falta esta funcionalidad es que es casi imposible construir un Level escriba para slf4j que se puede mapear de manera eficiente a la Level (o equivalente) tipo utilizado en todas las posibles implementaciones de registro detrás de la fachada. Alternativamente, los diseñadores decidieron que su caso de uso es demasiado inusual para justificar los gastos generales de apoyarlo.

Sobre @ ripper234es uso-caso (Pruebas unitarias), creo que la solución pragmática es modificar la (s) prueba (s) de la unidad para conocer con firmeza qué sistema de registro está detrás de la fachada slf4j ... cuando se ejecutan las pruebas unitarias.


36
2018-04-12 12:36



Richard Fearn tiene la idea correcta, así que escribí la clase completa en base a su código de esqueleto. Es de esperar lo suficientemente corto como para publicar aquí. Copie y pegue para disfrutar. Probablemente debería agregar un hechizo mágico también: "Este código se lanza al dominio público"

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



Puede implementar esto usando Java 8 lambdas.

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



Intenta cambiar a Logback y usa

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"));

Creo que esta será la única llamada a Logback y el resto de tu código permanecerá sin cambios. Logback usa SLF4J y la migración será sencilla, solo se tendrán que cambiar los archivos de configuración xml.

Recuerde volver a establecer el nivel de registro una vez que haya terminado.


9
2018-01-18 13:20



Esto se puede hacer con un enum y un método de ayuda:

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", ...);

Puedes agregar otras variantes de log, digamos si quería equivalentes genéricos de 1-parámetro o 2-parámetros de SLF4J warn/error/ etc. métodos.


6
2018-05-25 21:46



Cualquier persona que desee una solución completamente compatible con SLF4J para este problema, puede querer consultar Extensiones Lidalia SLF4J - Está en Maven Central.


4
2018-03-28 13:14



Acabo de encontrar una necesidad similar. En mi caso, slf4j está configurado con el adaptador de registro java (el jdk14 uno). Usando el siguiente fragmento de código, he logrado cambiar el nivel de depuración en tiempo de ejecución:

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



Basado en la respuesta de Massimo Virgilio, también he logrado hacerlo con slf4j-log4j usando la introspección. 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