使用log4j时,Logger.log(Priority p, Object message)
方法可用,可用于在运行时确定的日志级别记录消息。我们正在使用此事实和this tip将stderr重定向到特定日志级别的记录器。
slf4j没有我能找到的通用log()
方法。这是否意味着无法实现上述目标?
答案 0 :(得分:40)
使用slf4j
无法执行此操作。
我认为缺少此功能的原因是,几乎不可能为Level
构建slf4j
类型,可以有效地映射到Level
(或等价物) )在Facade后面的所有可能的日志记录实现中使用的类型。或者,设计者决定your use-case is too unusual来证明支持它的开销是合理的。
关于@ripper234的use-case(单元测试),我认为实用的解决方案是修改单元测试,以便了解slf4j外观背后的日志系统。 。运行单元测试时。
答案 1 :(得分:25)
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;
}
}
答案 2 :(得分:11)
您可以使用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);
}
}
答案 3 :(得分:11)
尝试切换到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,迁移将毫不费力,只需更改xml配置文件。
请记住在完成后重新设置日志级别。
答案 4 :(得分:6)
这可以使用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
/等的通用等价物。方法
答案 5 :(得分:5)
任何想要完全解决此问题的SLF4J兼容解决方案的人都可能需要查看Lidalia SLF4J Extensions - 它位于Maven Central上。
答案 6 :(得分:2)
我只需要类似的东西,然后想到了:
@RequiredArgsConstructor //lombok annotation
public enum LogLevel{
TRACE(l -> l::trace),
INFO (l -> l::info),
WARN (l -> l::warn),
ERROR(l -> l::error);
private final Function<Logger, Consumer<String>> function;
public void log(Logger logger, String message) {
function.apply(logger).accept(message);
}
}
用法:
LogLevel level = LogLevel.TRACE;
level.log(logger, "message");
记录器是在调用期间传递的,因此类信息应该可以,并且可以与@ Slf4j lombok注释很好地配合使用。
答案 7 :(得分:1)
在1.x中立即指定日志级别是不可能的。但是,希望slf4j 2.0
能够修复the issue。在2.0中,它可能看起来像这样:
// POTENTIAL 2.0 SOLUTION
import org.slf4j.helpers.Util;
import static org.slf4j.spi.LocationAwareLogger.*;
// does not work with slf4j 1.x
Util.log(logger, DEBUG_INT, "hello world!");
同时,对于slf4j 1.x,您可以使用以下解决方法:
将此类复制到您的类路径中:
import org.slf4j.Logger;
import java.util.function.Function;
public enum LogLevel {
TRACE(l -> l::trace, Logger::isTraceEnabled),
DEBUG(l -> l::debug, Logger::isDebugEnabled),
INFO(l -> l::info, Logger::isInfoEnabled),
WARN(l -> l::warn, Logger::isWarnEnabled),
ERROR(l -> l::error, Logger::isErrorEnabled);
interface LogMethod {
void log(String format, Object... arguments);
}
private final Function<Logger, LogMethod> logMethod;
private final Function<Logger, Boolean> isEnabledMethod;
LogLevel(Function<Logger, LogMethod> logMethod, Function<Logger, Boolean> isEnabledMethod) {
this.logMethod = logMethod;
this.isEnabledMethod = isEnabledMethod;
}
public LogMethod prepare(Logger logger) {
return logMethod.apply(logger);
}
public boolean isEnabled(Logger logger) {
return isEnabledMethod.apply(logger);
}
}
然后您可以像这样使用它:
Logger logger = LoggerFactory.getLogger(Application.class);
LogLevel level = LogLevel.ERROR;
level.prepare(logger).log("It works!"); // just message, without parameter
level.prepare(logger).log("Hello {}!", "world"); // with slf4j's parameter replacing
try {
throw new RuntimeException("Oops");
} catch (Throwable t) {
level.prepare(logger).log("Exception", t);
}
if (level.isEnabled(logger)) {
level.prepare(logger).log("logging is enabled");
}
这将输出如下日志:
[main] ERROR Application - It works!
[main] ERROR Application - Hello world!
[main] ERROR Application - Exception
java.lang.RuntimeException: Oops
at Application.main(Application.java:14)
[main] ERROR Application - logging is enabled
值得吗?
LogLevel
作为最小示例的源代码托管在on GitHub中。
答案 8 :(得分:1)
确认答案Ondrej Skopek
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;
var rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.TRACE);
您将获得结果:
2020-05-14 14:01:16,644跟踪[] [o.a.k.c.m.指标]测试人员 已注册的度量名为MetricName [name = bufferpool-wait-time-total, 组=生产者指标,描述=追加者等待的总时间 对于空间分配。,tags = {client-id = producer-2}]
答案 9 :(得分:0)
我刚遇到类似的需求。 在我的例子中,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");
答案 10 :(得分:0)
根据massimo virgilio的回答,我还设法使用内省来使用slf4j-log4j。 HTH。
data.table
答案 11 :(得分:0)
这是一个lambda解决方案,不像@Paul Croarkin那样用户友好(水平有效地传递了两次)。但我认为(a)用户应该通过Logger; (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[])
辅助方法。
答案 12 :(得分:0)
我能够通过首先请求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);
}
答案 13 :(得分:0)
我使用的方法是导入ch.qos.logback模块,然后将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类以查看 Level 的所有可能值:
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;
}
答案 14 :(得分:0)
使用slf4j API不可能动态更改日志级别,但是您可以自己配置日志备份(如果使用此选项)。在这种情况下,请为您的记录器创建工厂类,并使用所需的配置来实现root记录器。
LoggerContext loggerContext = new LoggerContext();
ch.qos.logback.classic.Logger root = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
// Configure appender
final TTLLLayout layout = new TTLLLayout();
layout.start(); // default layout of logging messages (the form that message displays
// e.g. 10:26:49.113 [main] INFO com.yourpackage.YourClazz - log message
final LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<>();
encoder.setCharset(StandardCharsets.UTF_8);
encoder.setLayout(layout);
final ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
appender.setContext(loggerContext);
appender.setEncoder(encoder);
appender.setName("console");
appender.start();
root.addAppender(appender);
配置根记录器后(只需一次即可),您可以委托获取新记录器,
final ch.qos.logback.classic.Logger logger = loggerContext.getLogger(clazz);
请记住使用相同的loggerContext
。
使用loggerContext
提供的root记录器可以轻松更改日志级别。
root.setLevel(Level.DEBUG);
答案 15 :(得分:0)
SLF4J v2.0 中的 fluent API 引入了一种新方法,即 Logger.makeLoggingEventBuilder(Level)
,可用于实现所需的结果。
示例代码:
public void logAMessageAtGivenLevel(Level aLevel, String aMessage) {
Logger logger = .. // some slf4j logger of choice
logger.makeLoggingEventBuilder(aLevel).log(aMessage);
}
如果对给定的 NOPLoggingEventBuilder
禁用记录器,则默认实现将返回 Level
的单例实例。正如名称 NOP 所示,LoggingEventBuilder
接口的这种实现什么都不做,为禁用的日志消息保留了纳秒级的执行时间。
答案 16 :(得分:-2)
使用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);
}
}
}
答案 17 :(得分:-6)
不,它有很多方法,info(),debug(),warn()等(这取代了优先级字段)
查看完整Logger api的http://www.slf4j.org/api/org/slf4j/Logger.html。