Sunday, October 10, 2010

Experience with slf4j/logback markers

I want to share my experience with slf4j/logback markers, hope can be useful for others.

Problem: want to have possibility to mail for particular log messages regardless log level.

Solution: Using slf4j/logback markers to mark :) interested messages and logback Filter.

First I've defined simple class to keep like global registry for slf4j markers:

public class LOGMarkers {
      public static final Marker SEND_MAIL_MARKER = MarkerFactory.getMarker("SEND_MAIL");
}

and here is code example which is actually using marker:

LOGGER.info(LOGMarkers.SEND_MAIL_MARKER, "Cannot save document with id: {}", instanceId);

or

try {
  .................
} catch (SomeException e) {
     LOGGER.warn(LOGMarkers.SEND_MAIL_MARKER, "", e);
}

also I have to write logback filters and use it in logback configuration file:

package info.sargis.logging.filter;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.AbstractMatcherFilter;
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

/**
* Created by IntelliJ IDEA.
* User: Sargis Harutyunyan
* Date: 10 oct. 2010
* Time: 20:12:12
*/
public class MarkerFilter extends AbstractMatcherFilter {

Marker markerToMatch;

public void start() {
     if (this.markerToMatch != null) {
     
     super.start();
     } else {
     
     addError(String.format("The marker property must be set for [%s]", getName()));
     }
}

public FilterReply decide(ILoggingEvent event) {
     Marker marker = event.getMarker();
     if (!isStarted()) {
     
     return FilterReply.NEUTRAL;
     }

     if (marker == null) {
     
     return onMismatch;
     }

     if (markerToMatch.contains(marker)) {
     
     return onMatch;
     }
     return onMismatch;
}

public void setMarker(String markerStr) {
     if (markerStr != null) {
     
     markerToMatch = MarkerFactory.getMarker(markerStr);
     }
}


}

 and finally logback config file example:


    <appender name="MARKER_EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
        <filter class="info.sargis.logging.filter.MarkerFilter">
            <marker>SEND_MAIL</marker>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
            <marker>SEND_MAIL</marker>
        </evaluator>
        <SMTPHost>localhost</SMTPHost>
        <To>sargis@localhost</To>
        <From>twm@localhost</From>
        <Subject>EPAYMAIL: %date %-5level - %message</Subject>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%date [%thread] %-5level U:%X{vaspId} - %message%n</Pattern>
        </layout>
    </appender>

    <logger name="info.sargis" level="INFO">
        <appender-ref ref="EPAYCONSOLE"/>
        <appender-ref ref="MARKER_EMAIL"/>
    </logger>

and voila :)

2 comments:

  1. Thank you for this informative article. The MarkerFilter is only useful for filtering out events which are not marked as SEND_MAIL_MARKER. It is *not* required for triggering an outgoing email.

    ReplyDelete
  2. You are completely right of course and my intention was exactly the same: to mark messages and after filter them before passing to ch.qos.logback.classic.net.SMTPAppender

    ReplyDelete