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

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

slf4j没有一个通用的log()方法,我可以find。 这是否意味着没有办法执行上述?

slf4j没有办法做到这slf4j

我想这个function缺失的原因是几乎不可能为slf4j构造一个Leveltypes,这个types可以有效地映射到Facade后面的所有可能的日志实现中使用的Level (或等价)types。 另外,devise者决定你的用例太不寻常了 ,不足以支持它的开销。

关于@ ripper234的用例 (unit testing),我认为实际的解决scheme是将unit testing修改为硬连接slf4j外观背后的测井系统的知识……运行unit testing时。

理查德·费恩(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; } } 

尝试切换到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的唯一调用,而其余的代码将保持不变。 Logback使用SLF4J,迁移将是无痛的,只是xmlconfiguration文件将不得不被改变。

请记住在完成后将日志级别设置回来。

这可以通过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 /等等的通用等价物。 方法。

您可以使用Java 8 lambdaexpression式来实现这一点。

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

任何人想要一个完全SLF4J兼容的解决scheme,以解决这个问题可能要检查Lidalia SLF4J扩展 – 这是在Maven中心。

我刚刚遇到类似的需求。 在我的情况下,slf4jconfiguration了java日志logging适配器(jdk14之一)。 使用下面的代码片段,我设法在运行时更改debugging级别:

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

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

这里的lambda解决scheme并不像@Paul Croarkin那样用户友好(这个级别有效地传递了两次)。 但是我认为(a)用户应该通过logging器; 和(b)AFAIU原来的问题并不是要求一个方便的方式在任何地方的应用程序,只有在一个图书馆内使用很less的情况。

 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 允许在可变参数参数中使用一个Throwable(它的堆栈跟踪应该被logging) ,所以我认为没有必要为(String, Object[])以外的其他使用者重载log帮助器方法。

我能够通过首先请求SLF4J Logger实例并在绑定上设置级别来为JDK14绑定执行此操作,您可以尝试使用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); } 

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

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

请查看http://www.slf4j.org/api/org/slf4j/Logger.html查看完整的Logger API。