SLF4J日志级别作为参数

18

我们想要使用SLF4J,但我们发现一个问题是无法通过参数指定日志级别,例如:

Logger.log(Level.INFO, "messsage");

你必须这样做

logger.info("message");

这样做可以防止将所有内容都通过一个方法传递,因此您可以在类中为所有日志消息添加其他属性。

public class Test
{
    public Test(SomeObj obj)
    {
       log(Level.INFO, "message");
    }

    public void anotherMethod()
    {
       log(Level.DEBUG, "another message");
    }
    private void log(Level level, String message)
    {
        logger.log(level, message + obj.someString());
    }
}
有没有使用SLF4j实现这个的方法?

2
传递字符串“INFO”和“DEBUG”,并使用反射调用正确的方法 - 只是开玩笑! - irreputable
1
事实上,现代大多数(可能全部)记录器仍然遵循那种古老的范式,这是非常短视和令人遗憾的。 - matanster
4个回答

11

编写一个 slf4j 调用的包装器并创建自己的六个日志级别枚举。然后在您的包装器中,使用 switch 调用正确的 slf4j 调用。

void myLog(Level level, String message)
{
  switch (level)
  {
  case FATAL:
    log.fatal(message);
    break;
  case ERROR:
    log.error(message);
    break;
  ....
  }
}

15
为什么没有一个 log(Level level, ...) 方法?(该方法是指在某些编程语言的日志库中可能存在的一种方法,用于在不同的日志级别下记录信息。) - Josh M.

7

有一种方法可以做到这一点,但它涉及获取 slf4j 找到的本地记录器的句柄。请参见下面的答案。 - PaulG

3

您的使用场景需要使用委托模式。基本上,您需要在您的代码和SLF4J之间嵌入自己的Logger实现,并“扩展”相关方法:

class MyLogger implements Logger {

    Logger realLogger;
    Object userData;


    MyLogger(Class clazz, Object userData){
        this.realLogger = LoggerFactory.getLogger(clazz);
    }

    public void debug(String msg) {
        realLogger.debug(msg + userData.someString());
    }

    // many more methods, perhaps per java.lang.reflect.Proxy
}

这是在业务代码中使用的方式:
public class Test
{
    Logger log;

    public Test(SomeObj obj)
    {
       log = new MyLogger(Test.class, obj);
       log.logInfo("message");
    }

    public void anotherMethod()
    {
       logDebug("another message");
    }
}

为了使 MyLogger 类可以最优化地重用,SomeObj 应该使用 Object.toString() 或者实现一个接口,该接口可以被 MyLogger 使用以获取消息附录。

1

从技术上讲,SLF4J并没有提供logger.log(Level, message)方法。但我找到了一个解决方法。[编辑:使用内省]

使用下面的代码片段,您可以在运行时获取slf4j找到并包装的本地记录器。如果您还记得,slf4j只是另一个提供程序(jdkLogging、Log4J、JCL等)中slf4j实现的包装器。所以在这里:

public Object getNativeLogger( org.slf4j.Logger logger ) {
   Object result = null;
   if ( logger.getClass().getName().equals("org.slf4j.impl.Log4jLoggerAdapter")) {
      try {
         Field f = Log4jLoggerAdapter.class.getDeclaredField("logger");
         f.setAccessible( true );
         result = (org.apache.log4j.Logger)f.get(logger);
      }
      catch( Exception e ) {
         System.out.println("Unable to access native log4j logger");
      }
   }
   else if ( logger.getClass().getName().equals("org.slf4j.impl.JDK14LoggerAdapter")) {
      try {
         Field f = Jdk14Logger.class.getDeclaredField("logger");
         f.setAccessible( true );
         result = (Jdk14Logger)f.get(logger);
      }
      catch( Exception e ) {
         System.out.println("Unable to access native log4j logger");
      }
   }
   else if (.....  other native loggers slf4j supports)....
   }
   return result;
}

然后你可以像这样使用它:

   Object l = getNativeLogger(mySlf4jLogger);
   if ( l instanceof org.apache.log4j.Logger ) {
      org.apache.log4j.Logger logger = (org.apache.log4j.Logger) l;
      logger.log( CUSTOMLog4JLevel, message);
   }
   else if( .... other implementations that you care about ...)...

所以,虽然它在技术上并不是 slf4j 内部的一部分,但可以使用 slf4j 作为主要日志接口来实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接