Is Logback the Solution to Your Logging Problems?

Reading Time: 4 minutes

What do you do when you get an error/issue with the code? What is the first thought that comes to your mind? What if you are trying to fix a code that was written by someone else?

Screenshot from 2019-12-30 11-24-23.png

 

That’s right. We check the logs. We all know that Useful logs can provide the developer ( especially when someone has to debug/maintain someone else’s code ) with tremendous help when trying to understand what the code actually does.

In programming languages, we make use of this mechanism using Logging.

Now that we all know what logging is, we can come down to the options that we have to enable and use logging in our scala code.
Some of the options/libraries that we can utilize for logging involve the following:

  • ScalaLogging
  • Log4j
  • Logback

and many more.

In our previous posts, we already have covered a few of the scala topics like Scala 2.13, Monads, Traits, etc. But in this post, we will keep our focus on logback about setting it up and using it in our project.

Logback is the successor of Log4j and is one of the better logging frameworks around. More importantly, logback’s architecture is sufficiently generic so as to apply under different circumstances.

To begin with, you need to include the following relevant dependency in your build.sbt:

libraryDependencies += “ch.qos.logback” % “logback-classic” % “1.0.1”

The next thing you will need is a logback.xml which will look something like this:

<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>src/main/resources/testFile.log</file>
<append>true</append>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} – %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>

view raw
logback.xml
hosted with ❤ by GitHub

In the above XML, I have added an example for a file appender. Using this appender we will be directly appending the desired logs to the file testFile.log in the above case.

Now, as we have got both the dependency and the logback.xml required, we can move on to adding the logs in our code. For that, we need to create an instance of ch.qos.logback.classic.Logger which we will be using for logging. For this, we can import the following :

import org.slf4j.LoggerFactory
import ch.qos.logback.classic.Logger

Once we have the imports we can create an instance of the Logger using:

val log: Logger = LoggerFactory.getLogger(getClass.getName).asInstanceOf[Logger]

Using this ​log as declared above, we can now log the required data based on the log level required in the following manner:

log.info("This is an Info Log By Anmol")
log.debug("This is a Debug Log By Anmol")
log.warn("This is a Warn Log By Anmol")
log.error("This is an Error Log By Anmol")

These log levels help us to track only specific logs at a time.

The order of the levels is as follows:
TRACE < DEBUG < INFO < WARN < ERROR.
Also, the below image states which levels would be displayed based on the choice of the level you choose.

logging-with-logback-in-scala-17-638

But we need to make the choice carefully and wisely as in which log should be put under which category. Also, we should avoid unnecessary logging as it just increases the code size and also makes the code less readable.

Screenshot from 2019-12-30 11-28-47
Well, all that said, it is important that these levels exist so that we can print different logs with our different requirements. It is also possible to modify the log level dynamically. We will be discussing that in the coming topics.

 

Adding an Appender Through Code

 

In the above part, we saw that we have a fully functioning logging functionality ready which could easily write the required logs in my desired file.

Screenshot from 2019-12-30 12-25-35.pngBut what if I need to dynamically add a logging functionality? Let’s say I need to add logs to the console but only when required.
For that, logback provides us with functionality to dynamically add an appender.
Yes, you read it correctly. Based on the requirement we can add an appender and use it as and when required.

You just need to create a small function using the following code:

def createConsoleAppender(): Unit = {
val lc: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]
val ca = new ConsoleAppender[ILoggingEvent]
ca.setContext(lc)
ca.setName("console")
val encoder = new LayoutWrappingEncoder[ILoggingEvent]
encoder.setContext(lc)
val layout = new PatternLayout
layout.setPattern("%d{HH:mm:ss.SSS} %-5level %logger{36} – %msg%n")
layout.setContext(lc)
layout.start()
encoder.setLayout(layout)
ca.setEncoder(encoder)
ca.start()
log.addAppender(ca)
}

Now all you need to do is just call this function and you will get all the console logs after this function call.

Note: Please note that the logs will only be printed after this function has been called as we will add the appender in this function only.

As we already discussed that we can also change the root log level through the code. For that, we can just call :

log.setLevel(Level.ERROR)

where the Level is nothing but ch.qos.logback.classic.Level which can be set to the various logging levels.

I hope that with all the above information, you guys are ready to use logback for your logging.

Screenshot from 2019-12-30 12-20-43.png
We have also uploaded the example for your reference that we discussed on the Github here.

Hope this blog helps. Feel free to drop any comments about the post & improvements if needed. Happy Logging. 🙂

 

 

Written by 

Anmol Sarna is a software consultant having more than 1.5 years of experience. He likes to explore new technologies and trends in the IT world. His hobbies include playing football, watching Hollywood movies and he also loves to travel and explore new places. Anmol is familiar with programming languages such as Java, Scala, C, C++, HTML and he is currently working on reactive technologies like Scala, Cassandra, Lagom, Akka, Spark and Kafka.