【问题标题】:SLF4J Log Level as an argument [duplicate]SLF4J日志级别作为参数[重复]
【发布时间】:2011-04-19 09:48:31
【问题描述】:

我们正在寻找使用 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 实现这一点?

【问题讨论】:

  • 传递字符串“INFO”和“DEBUG”,并使用反射调用正确的方法——开玩笑的!
  • 事实上,大多数(可能所有)当代伐木者仍然遵循这种古老的范式,这是非常短视和遗憾的

标签: java logging slf4j


【解决方案1】:

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

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 可以使用该接口来获取消息附录。

【讨论】:

    【解决方案2】:

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

    使用下面的代码 sn-p 您可以获得 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 作为您的主要日志接口。

    【讨论】:

      【解决方案3】:

      围绕 slf4j 调用编写一个包装器,并为六个日志级别创建自己的枚举。然后在你的包装器中,使用开关来调用正确的 slf4j 调用。

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

      【讨论】:

      • 怎么没有log(Level level, ...)方法?
      【解决方案4】:

      答案是否定的。请参考this discussion

      【讨论】:

      • 有一种方法可以做到这一点,但它涉及到获取 slf4j 找到的本机记录器的句柄。 -- 请看下面我的回答。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-25
      • 1970-01-01
      • 2011-02-06
      • 2013-11-27
      相关资源
      最近更新 更多