17

We are looking to use SLF4J, but one thing we found was that you can't specify the level as an argument, i.e

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

You have to do this

logger.info("message");

this prevents being able to pass everything through a method, so you can tack other properties to all log messages in a class.

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

Is there a way to achieve this using SLF4j ?

marchaos
  • 241
  • 1
  • 2
  • 5
  • 2
    pass string "INFO" and "DEBUG", and use reflection to invoke the correct method - just kidding! – irreputable Sep 30 '10 at 16:25
  • 1
    Indeed it is very shortsighted and regretful that most (probably all) contemporary loggers still follow that archaic paradigm – matanster Sep 15 '15 at 12:55

4 Answers4

11

Write a wrapper around the slf4j call and create your own enum for the six log levels. Then in your wrapper, use a switch to call the correct slf4j call.

void myLog(Level level, String message)
{
  switch (level)
  {
  case FATAL:
    log.fatal(message);
    break;
  case ERROR:
    log.error(message);
    break;
  ....
  }
}
Starkey
  • 9,673
  • 6
  • 31
  • 51
7

The answer is No. Refer to this discussion.

Community
  • 1
  • 1
Raghuram
  • 51,854
  • 11
  • 110
  • 122
  • There is a way to do it, but it involves getting a handle to the native logger that slf4j found. -- see my answer below. – PaulG Jul 20 '12 at 18:09
3

Your usecase screams for the delegation pattern. Basically you wedge your own implementation of Logger between your code and SLF4J and "extend" the relevant methods:

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
}

This is use in the business code like this:

public class Test
{
    Logger log;

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

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

For optimal reusability of the MyLogger class SomeObj should either use Object.toString() or it should implement an interface which MyLogger can use to get the message addendum.

A.H.
  • 63,967
  • 15
  • 92
  • 126
1

Well, technically SLF4J doesn't offer you a logger.log(Level, message) method. But I found a way around that. [edit: uses introspection]

Using the below code snippet you can get the native logger that slf4j found and wrapped for you at runtime. If you'll recall, slf4j is simply a wrapper around an slf4j implementation from another provider (either, jdkLogging, Log4J, JCL, etc...). So here:

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

Then you can use it like this:

   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 ...)...

So while it's not technically within slf4j, it is possible to do it using slf4j as your primary logging interface.

PaulG
  • 466
  • 1
  • 6
  • 19