Уровень журнала SLF4J как аргумент

Мы хотим использовать 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?

Ответ 1

Напишите оболочку вокруг вызова slf4j и создайте свой собственный перечисление для шести уровней журнала. Затем в вашей обертке используйте переключатель для вызова правильного вызова slf4j.

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

Ответ 3

Ваш файл usecase кричит для шаблона делегирования. В основном вы вклиниваете свою собственную реализацию 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 может использовать для получения добавления сообщения.

Ответ 4

Ну, технически SLF4J не предлагает вам метод logger.log(уровень, сообщение). Но я нашел способ обойти это. [edit: использует интроспекцию]

Используя приведенный ниже фрагмент кода, вы можете получить собственный регистратор, который slf4j нашел и обернул для вас во время выполнения. Если вы вспомните, slf4j - это просто оболочка реализации slf4j от другого провайдера (либо jdkLogging, Log4J, JCL, и т.д.). Итак, здесь:

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 в качестве основного интерфейса ведения журнала.