Question

I like scala as it can help me have cleaner code.

However while logging i feel my code gets cluttered...

def myFunc(args) = {
  log.trace("entering myfunc, args are ... ")
  val result = doSomething()
  log.info("I really want to print that result which is {}", result)
  log.trace("exiting myfunc result {}", result)
  result
}

as you can see instead of just calling

def myFunc(args) = doSoemthing

I needed to split the result into a var so that i can first log it and only then return it. now that was a simple use case in more complex use cases I have more loggings in my methods, some info some debug things become more complex and code becomes much more cluttered due to logging..

while I could wrap my method with another method which would add logging on entry and exit this would make my code more complex so i'm looking for a very clean logging solution not focusing only on method entry and exit (I dont want to use AOP because it makes code also more compelx and hard to understand).

how to unclutter code while still having code simple and easy to understand with powerful logging? (solution not invented yet?) or is there a true clean solution?

Was it helpful?

Solution

First of all, nothing prevent you from defining that result as val, not var. Next, there is such thing as kestrel combinator (see also linked answer specifically on logging topic), which is usually used for such purporses:

def myFunc(args) = {
  log.trace("entering myfunc, args are ... ")
  doSomething().tap { result => 
    log.info("I really want to print that result which is {}", result)
    log.trace("exiting myfunc result {}", result)
  }
}

Next, I do believe there is similar to AOP methods, like scala virtualized (which allow you to pimp basic language syntax), but it feels like huge overkill to me, I guess you can roll out a set of helpers and use them as:

traced(foo) { x => 
  myFunc(x) 
}

so input and output will be logged

OTHER TIPS

This question has been answered and accepted already, but there's another solution in the limited context of debugging; and it can be probably combined with existing solutions.

Full disclosure: I've developed Scart, so I am biased; whether or not it's elegant is a question of taste!

If you want to perform tracing, as opposed to logging, i.e. emit debugging info for developers and do not want that in the deployed product, you can try Scart's Expression Tracers.

Actually, they came to existence for the reason you mentioned; to some extent they can be inserted on the right side of the source code, therefore the clutter doesn't disappear but is less intrusive. At least that works for me.

I haven't tried the following, but in your use case that may look like:

def myFunc(args) = {                             
  val result = doSomething(args)
  log.info("I really ... which is {}", result)
  result                                             e_++: s"myFunc($args)"
}

I left the log info, otherwise:

def myFunc(args) = doSomething(args)                 e_++: s"myFunc($args)"

or rather (Scart uses macros; the compiler gives a warning if this trace is off):

def myFunc(args) =                                   s"myFunc($args)".e_++:
{
  doSomething(args)
}

You'll note that I didn't write lengthy trace strings, but Scart automatically adds information such as class, method names, line# etc. Since it's for debugging, I personally refer to those and don't need to write very long strings.

There are more options, eventually I ought to document them all.

Also, Scart doesn't require a logger to operate. In order to output its traces within logs, you'll have to designate a printer function that uses a logger (in the Scart settings object Settings) which is of type String => Unit.

Cheers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top