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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2621701/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:59:09  来源:igfitidea点击:

Setting log level of message at runtime in slf4j

javalogginglog4jslf4j

提问by Edward Dale

When using log4j, the Logger.log(Priority p, Object message)method is available and can be used to log a message at a log level determined at runtime. We're using this fact and this tipto redirect stderr to a logger at a specific log level.

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

slf4j doesn't have a generic log()method that I can find. Does that mean there's no way to implement the above?

slf4j 没有log()我能找到的通用方法。这是否意味着无法实现上述内容?

采纳答案by Stephen C

There is no way to do this with slf4j.

没有办法做到这一点slf4j

I imagine that the reason that this functionality is missing is that it is next to impossible to construct a Leveltype for slf4jthat can be efficiently mapped to the Level(or equivalent) type used in all of the possible logging implementations behind the facade. Alternatively, the designers decided that your use-case is too unusualto justify the overheads of supporting it.

我认为缺少此功能的原因是几乎不可能构造一种Level类型,slf4j该类型可以有效地映射到LevelFacade 后面所有可能的日志实现中使用的(或等效的)类型。或者,设计人员认为您的用例太不寻常,无法证明支持它的开销是合理的。

Concerning @ripper234's use-case(unit testing), I think the pragmatic solution is modify the unit test(s) to hard-wire knowledge of what logging system is behind the slf4j facade ... when running the unit tests.

关于@ripper234用例(单元测试),我认为实用的解决方案是修改单元测试,以了解 slf4j 外观背后的日志记录系统......在运行单元测试时。

回答by chris

no, it has a number of methods, info(), debug(), warn(), etc (this replaces the priority field)

不,它有许多方法,info()、debug()、warn() 等(这取代了优先级字段)

have a look at http://www.slf4j.org/api/org/slf4j/Logger.htmlfor the full Logger api.

查看http://www.slf4j.org/api/org/slf4j/Logger.html以获得完整的 Logger api。

回答by Richard Fearn

This can be done with an enumand a helper method:

这可以通过一个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", ...);

You could add other variants of log, say if you wanted generic equivalents of SLF4J's 1-parameter or 2-parameter warn/error/etc. methods.

您可以添加的其他变体log,比方说,如果你想SLF4J的1参数或2个参数的一般等价物warn/error的/ etc。方法。

回答by David Tonhofer

Richard Fearn has the right idea, so I wrote up the full class based on his skeleton code. It's hopefully short enough to post here. Copy & paste for enjoyment. I should probably add some magic incantation, too: "This code is released to the public domain"

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

回答by massimo virgilio

using java introspection you can do it, for example:

使用 java introspection 你可以做到,例如:

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

}

回答by Αλ?κο?

Try switching to Logback and use

尝试切换到 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"));

I believe this will be the only call to Logback and the rest of your code will remain unchanged. Logback uses SLF4J and the migration will be painless, just the xml config files will have to be changed.

我相信这将是对 Logback 的唯一调用,其余代码将保持不变。Logback 使用 SLF4J 并且迁移将是无痛的,只需要更改 xml 配置文件。

Remember to set the log level back after you're done.

请记住在完成后重新设置日志级别。

回答by Robert Elliot

Anyone wanting a drop-in fully SLF4J compatible solution to this problem might want to check out Lidalia SLF4J Extensions- it's on Maven Central.

任何想要一个完全兼容 SLF4J 的解决方案来解决这个问题的人都可能想要查看Lidalia SLF4J 扩展——它在 Maven Central 上。

回答by Yair Zaslavsky

I have just encountered a similar need. In my case, slf4j is configured with the java logging adapter (the jdk14 one). Using the following code snippet I have managed to change the debug level at runtime:

我刚刚遇到了类似的需求。就我而言,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");

回答by Guido

Based on the answer of massimo virgilio, I've also managed to do it with slf4j-log4j using introspection. HTH.

根据 massimo virgilio 的回答,我还使用自省的 slf4j-log4j 设法做到了。哈。

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

回答by Paul Croarkin

You can implement this using Java 8 lambdas.

您可以使用 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);
    }
}