« Return to Thread: logging the Scala way and named varargs

Re: logging the Scala way and named varargs

by Blair Zajac :: Rate this Message:

Reply to Author | View in Thread

On Jun 17, 2009, at 9:57 PM, Walter Smith wrote:

>
> Hi!
>
> I think logging should be a part of the core libraries of a  
> language... it
> is such an unbeloved matter that at least the logging api to use  
> should not
> be something to discuss about in every project.
>
> There is one logging framework built into Java, but it is not very  
> well
> received. Especially in the Java enterprise world, log4j is used  
> much more
> often for some good reasons, but you can't deploy multiple  
> configurations
> for the same classes used in different enterprise applications  
> within one
> JVM, if it's part of the parent class loader, i.e. it's used by the
> container itself. And there is a third comprehensive logging framework
> competing: logback. So it's a good idea for frameworks to be able to  
> use a
> generic logging api, so that a customer can use *one* framework for  
> all the
> frameworks in use. And generally Scala is right now in the role of  
> such a
> framework, isn't it?
>
> I still see Apache commons logging being used a lot. I think this is  
> not
> much better than using prinln directly! It not only lacks important  
> features
> like formatting messages only when needed, or MDCs; I understand  
> that it
> still causes perm gen memory leaks when hot deploying.
>
> There is a much better solution, though: SLF4J... IMHO every Java or  
> Scala
> programmer should know it.
>
> But SLF4J is a Java api, so I tried to wrap it in "the Scala way":
> 1. Logging is a trait that defines the logger using the name of the  
> class
> you mix it into; a common and regularly used pattern now easily  
> provided by
> a library... that's what I love Scala for.
> 2. There is a sealed trait LogLevel with objects for error, info,  
> etc. that
> you can pass around, e.g. into a library or to configure instances  
> of a
> class. To actually log at a LogLevel, you simply apply your  
> parameters to
> it; the logger can be passed in as a implicit argument.
> 3. The arguments that are used for your format are passed in by  
> name, so
> only get evaluated when the log level is enabled, i.e. it's ok to call
> debug("client dns name {}",  
> java.net.InetAddress.getByAddress(client)); the
> lookup will only be made, if the debug log level is enabled... Scala  
> rules!
>
> BTW: Is there a simpler way to get an Array[Byte] literal than
> Array(192.byteValue,168.byteValue,178.byteValue,1.byteValue)?

The point of slf4j's format string, i.e. "The foobar's value is {}",  
is to avoid interpolation cost if the log message will not be used.

The Scala way of dealing with this is to use by-name arguments.  So  
you end up with

trait Logger
{
   def warn(msg : => String) : Unit
}

trait Slf4jLogger extends Logger
{
   private val myRealLogger = Logger.getLogger(this.getClass.getName)

   private val isWarnEnabled = myRealLogger.isWarnEnabled

   def warn(msg : => String) : Unit
   {
     if (isWarnEnabled) {
       myRealLogger.warn(msg)
     }
   }
}

Then call it

   logger.warn("The foobar's value is " + foobar + ".")

the string concatenation here won't be done unless  
my_real_logger.isWarnEnabled is true.

BTW, I'm not a fan of implicits here.  I'd much rather see a call

   logger.warn("It blew up!")

than

   warn("It blew up!")

It's clear I'm calling a logger and don't need to wonder where warn()  
is defined.

Check out Lift's logger.  It does this nicely and has a slf4j layer.

http://github.com/dpp/liftweb/blob/338119b8d7a76adcb9f45e9aa8a2b946d9c81118/lift-util/src/main/scala/net/liftweb/util/Log.scala
http://github.com/dpp/liftweb/blob/338119b8d7a76adcb9f45e9aa8a2b946d9c81118/lift-util/src/main/scala/net/liftweb/util/Slf4jLog.scala

Regards,
Blair

--
Blair Zajac, Ph.D.
CTO, OrcaWare Technologies
<blair@...>
Subversion training, consulting and support
http://www.orcaware.com/svn/


 « Return to Thread: logging the Scala way and named varargs