代码之家  ›  专栏  ›  技术社区  ›  Edward Dale

在SLF4J中设置运行时消息的日志级别

  •  80
  • Edward Dale  · 技术社区  · 15 年前

    使用log4j时, Logger.log(Priority p, Object message) 方法可用,可用于在运行时确定的日志级别记录消息。我们利用这个事实 this tip 将stderr重定向到特定日志级别的记录器。

    SLF4J没有通用的 log() 我能找到的方法。这是否意味着没有办法实现上述目标?

    13 回复  |  直到 6 年前
        1
  •  39
  •   Community CDub    8 年前

    没办法这么做 slf4j .

    我认为这个功能缺失的原因是几乎不可能构造 Level 类型为 SLF4J 可以有效地映射到 水平 (或等效)在facade后面的所有可能的日志实现中使用的类型。或者,设计师决定 your use-case is too unusual 来证明支持它的开销是合理的。

    关于 @ripper234 use-case (单元测试),我认为实际的解决方案是修改单元测试,以硬连线了解slf4j facade背后的日志系统……运行单元测试时。

        2
  •  25
  •   David Tonhofer    13 年前

    Richard Fearn有正确的想法,所以我根据他的框架代码编写了完整的类。希望短到可以贴在这里。复制粘贴以获得乐趣。我可能也应该加上一些咒语:“这段代码已经发布到公共域”

    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;
        }
    }
    
        3
  •  11
  •   Paul Croarkin    9 年前

    您可以使用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);
        }
    }
    
        4
  •  11
  •   Ondrej Skopek    8 年前

    尝试切换到Logback并使用

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

    我相信这将是唯一一个需要注销的调用,剩下的代码将保持不变。logback使用slf4j,迁移将是无痛的,只需更改xml配置文件。

    完成后请记住将日志级别设置回原来的水平。

        5
  •  6
  •   Richard Fearn    14 年前

    这可以用一个 enum 以及一个助手方法:

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

    您可以添加其他变体 log ,假设您想要slf4j的1参数或2参数的泛型等价物 warn / error /等方法。

        6
  •  5
  •   Robert Elliot    11 年前

    任何人想要一个完全兼容SLF4J的解决方案来解决这个问题,可能想看看 Lidalia SLF4J Extensions -在马文中心。

        7
  •  1
  •   Yair Zaslavsky    11 年前

    我也遇到过类似的需求。 在我的例子中,SLF4J是用Java日志适配器(JDK14)配置的。 使用下面的代码段,我成功地在运行时更改了调试级别:

    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");
    
        8
  •  0
  •   Guido    9 年前

    基于Massimo Virgilio的回答,我还成功地用SLF4J-log4J进行了内省。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());
    }
    
        9
  •  0
  •   EndlosSchleife    9 年前

    这里有一个lambda解决方案,在某种程度上不如@paul croarkin的用户友好(这个级别实际上通过了两次)。但我认为(a)用户应该通过记录器;和(b)afaiu最初的问题不是要为应用程序中的任何地方提供方便的方式,而是在库中很少使用的情况下。

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

    自SLF4J以来 allows a Throwable (whose stack trace should be logged) inside the varargs param ,我认为不需要重载 log 其他使用者的助手方法 (String, Object[]) .

        10
  •  0
  •   youurayy    8 年前

    我通过首先请求slf4j日志实例和 然后 设置绑定的级别——您可以在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);
    }
    
        11
  •  0
  •   Glenn Inn    6 年前

    我使用的方法是导入ch.qos.logback模块,然后键入cast将slf4j logger实例转换为ch.qos.logback.classic.logger。此实例包含一个setlevel()方法。

    import ch.qos.logback.classic.Level;
    import ch.qos.logback.classic.Logger;
    
    Logger levelSet = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    
    // Now you can set the desired logging-level
    levelSet.setLevel( Level.OFF );
    

    要找出可能的日志记录级别,可以分解ch.qos.logback类以查看 水平 :

    prompt$ javap -cp logback-classic-1.2.3.jar ch.qos.logback.classic.Level
    

    结果如下:

    {
       // ...skipping
       public static final ch.qos.logback.classic.Level OFF;
       public static final ch.qos.logback.classic.Level ERROR;
       public static final ch.qos.logback.classic.Level WARN;
       public static final ch.qos.logback.classic.Level INFO;
       public static final ch.qos.logback.classic.Level DEBUG;
       public static final ch.qos.logback.classic.Level TRACE;
       public static final ch.qos.logback.classic.Level ALL;
    }
    
        12
  •  -1
  •   massimo virgilio    13 年前

    使用Java自省,你可以这样做,例如:

    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);
            }
        }
    
    }
    
        13
  •  -5
  •   chris    15 年前

    不,它有许多方法,info()、debug()、warn()等(这将替换priority字段)

    看一看 http://www.slf4j.org/api/org/slf4j/Logger.html 对于完整的记录器API。