Showing posts with label Groovy. Show all posts
Showing posts with label Groovy. Show all posts

Loggers and appenders

After performing some basic Logback configuration, we can see how to set appenders and loggers, so that Logback could send its output both to console and a file, specifying a different behavior in different classes.

We have already know how to use an appender to send output to the console, now we see how to create a file appender, and also how to create a configuration logger, to describe a relation between a SLF4J logger, as defined and used in our code, and an appender.

Here is an XML Logback configuration file:
<configuration debug="true">
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
  <appender name="CApp" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}: %msg%n</pattern>
    </encoder>
  </appender>
  <appender name="FApp" class="ch.qos.logback.core.FileAppender">
    <file>TestLogback.log</file>
    <encoder>
      <pattern>%date %level [%thread] %logger{10} [%file:%line]: %msg%n</pattern>
    </encoder>
  </appender>
  <logger name="test.TestLogback" level="info">
    <appender-ref ref="FApp" />
  </logger>
  <root level="trace">
    <appender-ref ref="CApp" />
  </root>
</configuration>
You see that this configuration file is set to be in debug mode, and it has a status listener element set to output its feedback to the standard console. More on Logback configuration debug in the previous post.
Same for the CApp appender, console appender already seen in the previous post.
The FApp appender is a FileAppender. We specify its name and the format internally used.
The logger element works like a root element, the biggest difference is due that root does not need a name, while a logger has to provide it, to be used by the SLF4J logger factory to retrieve the settings.
We can specify the additivity attribute in the logger, defaulted to true, if not explicitly set to false, it means that this logger uses the appenders specified by its ancestors (if any) and adds the one(s) specified in this element's belly. False means that all the existing appenders are discarded and only the one specified there are used.
The logger level is explicitly set to "info", this logger and all the ones on hierarchy below are affected by this.

We can get the same result by a groovy configuration file:
import static ch.qos.logback.classic.Level.DEBUG
import static ch.qos.logback.classic.Level.INFO
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.status.OnConsoleStatusListener
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.FileAppender

statusListener(OnConsoleStatusListener)

appender("CApp", ConsoleAppender) {
  encoder(PatternLayoutEncoder) {
    pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}: %msg%n"
  }
}

appender("FApp", FileAppender) {
  file = "TestLogback.log";
  encoder(PatternLayoutEncoder) {
    pattern = "%date %level [%thread] %logger{10} [%file:%line]: %msg%n"
  }
}

logger("test.TestLogback", INFO, ["FApp"])
root(DEBUG, ["CApp"])
The lack of semicolons at the end of lines is a perlish Groovy variation on Java syntax.

Go to the full post

Autoscanning Logback configuration

A reason for breaking the decoupling between SLF4J and the underlying concrete logging framework is for changing the current log configuration without having to restart the application.

Logback has a feature that could be used to get a similar result leaving all the configuration stuff in a file. The idea is specifying in the configuration if Logback has to check again the configuration file and, if a change is detected, reload the properities.

This is achieved adding a couple of attributes to the configuration element for XML files (scan="true" scanPeriod="30 seconds" in my case, where I wanted to rescan the file each half minute - default value for scanPeriod is one minute).
For Groovy configuration we just have to add A call to scan("30 seconds").

I had a bumpy beginning of a relation with this feature, I couldn't make it work. But this was a good way to keep in touch with the internal Logback debug setting to get information on what was going.

Here is the XML configuration file that I used to test the autoscanning feature:
<configuration scan="true" scanPeriod="30 seconds" debug="true">
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
  <appender name="capp" class="ch.qos.logback.core.ConsoleAppender"> 
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> 
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{40}: %msg%n</pattern> 
    </encoder> 
  </appender>
  <root level="all"> 
    <appender-ref ref="capp" /> 
  </root> 
</configuration>
The configuration element has three attributes, the first two ones for scanning and the third, named "debug", set to true, to let Logback giving debug feedback. I couldn't find how I could do the same in a Groovy configuration file (if someone knows, please, drop a comment).
Then I added a statusListener element specifying as associated class the one to send output to the standard console, and that was it.

Go to the full post

Configuring Logback

At startup Logback checks if there is a configuration file in the application classpath. First choice is logback.groovy, logback-test.xml and logback.xml are the fallback options. If none of these files is available, a default configuration is used, and the output is sent to standard console.

Here is an XML configuration file that would give a result close to the default.:
<configuration>
    <appender name="CApp" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{12}: %msg%n</pattern>
        </encoder>
    </appender>
    <root level="trace">
        <appender-ref ref="CApp" />
    </root>
</configuration>
The appender element describes a logging destination. Here the standard output console is used.
In the appender element is defined its associated encoder, where we define the pattern we want to use in the log generation.
The pattern describes a string where the first field is the time of current log line generation, the second is the thread name where the log was generated, the third is the log level, fourth is the logger name (with the max number of significative character that should be used - twelve is unreasonable low, but it could make sense in such a silly test). Finally we have the log message itself and a newline. The resulting pattern is very close to the default one.
The root element describes the root logger, that has a log level set to trace (instead of the default debug). The appender element inside root is where we can specify which appender (here CApp) should be used in this context.

It is easy to rewrite the same configuration with Groovy:
appender("CApp", ch.qos.logback.core.ConsoleAppender) {
    encoder(ch.qos.logback.classic.encoder.PatternLayoutEncoder) {
        pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{12}: %msg%n"
    }
}
root(ch.qos.logback.classic.Level.TRACE, ["CApp"])
Similar structure, less verbose, and much more flexibility. The only issue is that we need Groovy in the application classpath.

Go to the full post