Definir o nível de registo da mensagem no tempo de execução no slf4j

ao usar o log4j, o método Logger.log(Priority p, Object message) está disponível e pode ser usado para registar uma mensagem a um nível de Registo determinado no tempo de execução. Estamos a usar este facto e Esta dica para redireccionar o stderr para um logger a um nível de registo específico.

O Slf4j não tem um método genérico log() que eu possa encontrar. Isso significa que não há maneira de implementar o acima?

Author: scompt.com, 2010-04-12

12 answers

Não há maneira de fazer isto com slf4j.

Imagino que a razão pela qual esta funcionalidade está em falta é que é quase impossível construir um tipo Level Para slf4j que possa ser mapeado eficientemente para o tipo Level (ou equivalente) usado em todas as possíveis implementações de registo por trás da fachada. Em alternativa, os designers decidiram que o seu estojo de uso é demasiado invulgar para justificar as despesas gerais do seu apoio.

Referente a @ripper234 ' s

 37
Author: Stephen C, 2017-05-23 12:34:18
O Richard Fearn tem a ideia certa, por isso escrevi a turma toda com base no seu código-esqueleto. Espero que seja curta o suficiente para postar aqui. Copiar e colar para desfrutar. Eu provavelmente deveria adicionar um encantamento mágico, também:"este código é liberado para o domínio 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;
    }
}
 22
Author: David Tonhofer, 2011-10-19 23:48:05

Você pode implementar isto 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);
    }
}
 11
Author: Paul Croarkin, 2016-03-10 17:01:06

Tente mudar para Logback e usar

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"));
Acredito que esta será a única chamada para o Logback e o resto do seu código permanecerá inalterado. Logback usa SLF4J e a migração será indolor, apenas os arquivos de configuração xml terá que ser alterado. Lembre-se de repor o nível de registo depois de terminar.
 10
Author: Αλέκος, 2017-01-14 00:20:30

Isto pode ser feito com um enum e um método auxiliar:

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

Pode adicionar outras variantes de log, digamos que se quiser equivalentes genéricos do parâmetro 1 ou parâmetro 2 do SLF4J warn/error/etc. meios.

 6
Author: Richard Fearn, 2011-05-25 21:46:53

Quem quiser uma solução totalmente compatível com o SLF4J para este problema pode querer verificar As Extensões Lidalia SLF4J - é no centro Maven.

 5
Author: Robert Elliot, 2014-03-28 13:14:48
Acabei de encontrar uma necessidade semelhante. No meu caso, o slf4j está configurado com o adaptador de Registo java (o jdk14). Usando o seguinte excerto de código, consegui alterar o nível de depuração na execução:
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
Author: Yair Zaslavsky, 2014-06-11 03:33:07
Baseado na resposta de massimo virgilio, também consegui fazê-lo com slf4j-log4j usando introspecção. 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
Author: Guido, 2016-02-24 12:39:51

Aqui está uma solução lambda não tão fácil de usar como @Paul Croarkin de uma maneira (o nível é efetivamente passado duas vezes). Mas eu acho que (a) o Usuário deve passar o Logger; e (B) AFAIU a pergunta original não estava pedindo uma maneira conveniente para todos os lugares na aplicação, apenas uma situação com poucos usos dentro de uma biblioteca.

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

Uma vez que o slf4j permite uma descarga (cujo traço de pilha deve ser registado) dentro do varargs param , penso que não há necessidade de sobrecarregar o método auxiliar log para outros consumidores que não (String, Object[]).

 0
Author: EndlosSchleife, 2016-07-29 09:30:45

Fui capaz de fazer isto para a ligação JDK14, solicitando primeiro a instância de Logger SLF4J e Depois a definir o nível na ligação -- pode tentar isto para a ligação Log4J.

private void setLevel(Class loggerClass, java.util.logging.Level level) {
  org.slf4j.LoggerFactory.getLogger(loggerClass);
  java.util.logging.Logger.getLogger(loggerClass.getName()).setLevel(level);
}
 0
Author: youurayy, 2017-06-21 00:50:05

Usando a introspecção java, pode fazê-lo, por exemplo:

private void changeRootLoggerLevel(int level) {

    if (logger instanceof org.slf4j.impl.Log4jLoggerAdapter) {
        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.log4j.Logger loggerImpl = (org.apache.log4j.Logger) fields[i]
                            .get(logger);

                    if (level == DIAGNOSTIC_LEVEL) {
                        loggerImpl.setLevel(Level.DEBUG);
                    } else {
                        loggerImpl.setLevel(org.apache.log4j.Logger.getRootLogger().getLevel());
                    }

                    // fields[i].setAccessible(false);
                }
            }
        } catch (Exception e) {
            org.apache.log4j.Logger.getLogger(LoggerSLF4JImpl.class).error("An error was thrown while changing the Logger level", e);
        }
    }

}
 -1
Author: massimo virgilio, 2012-04-27 15:45:10

Não, tem uma série de métodos, info(), debug (), warn (), etc (Isto substitui o campo de prioridade)

Olha para http://www.slf4j.org/api/org/slf4j/Logger.html para a api De Logger completa.

 -4
Author: oedo, 2010-04-12 11:42:45