聊聊logback的MarkerFilter

本文主要研究一下logback的MarkerFilter

MarkerFilter

ch/qos/logback/classic/turbo/MarkerFilter.java

public class MarkerFilter extends MatchingFilter {

    Marker markerToMatch;

    @Override
    public void start() {
        if (markerToMatch != null) {
            super.start();
        } else {
            addError("The marker property must be set for [" + getName() + "]");
        }
    }

    @Override
    public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
        if (!isStarted()) {
            return FilterReply.NEUTRAL;
        }

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

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

    /**
     * The marker to match in the event.
     * 
     * @param markerStr
     */
    public void setMarker(String markerStr) {
        if (markerStr != null) {
            this.markerToMatch = MarkerFactory.getMarker(markerStr);
        }
    }
}

MarkerFilter定义了markerToMatch属性,其decide方法对于传入的marker为null返回onMismatch,对于包含markerToMatch的返回onMatch,否则返回onMismatch

MarkerFactory

org/slf4j/MarkerFactory.java

public class MarkerFactory {
    static IMarkerFactory MARKER_FACTORY;

    private MarkerFactory() {
    }

    // this is where the binding happens
    static {
        SLF4JServiceProvider provider = LoggerFactory.getProvider();
        if (provider != null) {
            MARKER_FACTORY = provider.getMarkerFactory();
        } else {
            Util.report("Failed to find provider");
            Util.report("Defaulting to BasicMarkerFactory.");
            MARKER_FACTORY = new BasicMarkerFactory();
        }
    }

    /**
     * Return a Marker instance as specified by the name parameter using the
     * previously bound {@link IMarkerFactory}instance.
     * 
     * @param name
     *          The name of the {@link Marker} object to return.
     * @return marker
     */
    public static Marker getMarker(String name) {
        return MARKER_FACTORY.getMarker(name);
    }

    /**
     * Create a marker which is detached (even at birth) from the MarkerFactory.
     *
     * @param name the name of the marker
     * @return a dangling marker
     * @since 1.5.1
     */
    public static Marker getDetachedMarker(String name) {
        return MARKER_FACTORY.getDetachedMarker(name);
    }

    /**
     * Return the {@link IMarkerFactory}instance in use.
     * 
     * 

The IMarkerFactory instance is usually bound with this class at * compile time. * * @return the IMarkerFactory instance in use */ public static IMarkerFactory getIMarkerFactory() { return MARKER_FACTORY; } }

MarkerFactory通过static方法来初始化MARKER_FACTORY,若SLF4JServiceProvider不为null则取provider.getMarkerFactory(),否则取BasicMarkerFactory

BasicMarkerFactory

org/slf4j/helpers/BasicMarkerFactory.java

public class BasicMarkerFactory implements IMarkerFactory {

    private final ConcurrentMap markerMap = new ConcurrentHashMap<>();

    /**
     * Regular users should not create
     * BasicMarkerFactory instances. Marker
     * instances can be obtained using the static {@link
     * org.slf4j.MarkerFactory#getMarker} method.
     */
    public BasicMarkerFactory() {
    }

    /**
     * Manufacture a {@link BasicMarker} instance by name. If the instance has been 
     * created earlier, return the previously created instance. 
     * 
     * @param name the name of the marker to be created
     * @return a Marker instance
     */
    public Marker getMarker(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Marker name cannot be null");
        }

        Marker marker = markerMap.get(name);
        if (marker == null) {
            marker = new BasicMarker(name);
            Marker oldMarker = markerMap.putIfAbsent(name, marker);
            if (oldMarker != null) {
                marker = oldMarker;
            }
        }
        return marker;
    }

    /**
     * Does the name marked already exist?
     */
    public boolean exists(String name) {
        if (name == null) {
            return false;
        }
        return markerMap.containsKey(name);
    }

    public boolean detachMarker(String name) {
        if (name == null) {
            return false;
        }
        return (markerMap.remove(name) != null);
    }

    public Marker getDetachedMarker(String name) {
        return new BasicMarker(name);
    }

}

BasicMarkerFactory通过ConcurrentMap来存储string与Marker的映射,创建的是BasicMarker

Marker

org/slf4j/Marker.java

public interface Marker extends Serializable {

    /**
     * This constant represents any marker, including a null marker.
     */
    public final String ANY_MARKER = "*";

    /**
     * This constant represents any non-null marker.
     */
    public final String ANY_NON_NULL_MARKER = "+";

    /**
     * Get the name of this Marker.
     * 
     * @return name of marker
     */
    public String getName();

    /**
     * Add a reference to another Marker.
     *
     * 

Note that the fluent API allows adding multiple markers to a logging statement. * It is often preferable to use multiple markers instead of nested markers. *

* * @param reference * a reference to another marker * @throws IllegalArgumentException * if 'reference' is null */ public void add(Marker reference); /** * Remove a marker reference. * * @param reference * the marker reference to remove * @return true if reference could be found and removed, false otherwise. */ public boolean remove(Marker reference); /** * @deprecated Replaced by {@link #hasReferences()}. */ @Deprecated public boolean hasChildren(); /** * Does this marker have any references? * * @return true if this marker has one or more references, false otherwise. */ public boolean hasReferences(); /** * Returns an Iterator which can be used to iterate over the references of this * marker. An empty iterator is returned when this marker has no references. * * @return Iterator over the references of this marker */ public Iterator iterator(); /** * Does this marker contain a reference to the 'other' marker? Marker A is defined * to contain marker B, if A == B or if B is referenced by A, or if B is referenced * by any one of A's references (recursively). * * @param other * The marker to test for inclusion. * @throws IllegalArgumentException * if 'other' is null * @return Whether this marker contains the other marker. */ public boolean contains(Marker other); /** * Does this marker contain the marker named 'name'? * * If 'name' is null the returned value is always false. * * @param name The marker name to test for inclusion. * @return Whether this marker contains the other marker. */ public boolean contains(String name); /** * Markers are considered equal if they have the same name. * * @param o * @return true, if this.name equals o.name * * @since 1.5.1 */ public boolean equals(Object o); /** * Compute the hash code based on the name of this marker. * Note that markers are considered equal if they have the same name. * * @return the computed hashCode * @since 1.5.1 */ public int hashCode(); }

Marker接口定义了getName、add、remove、hasReferences、iterator、contains、equals、hashCode方法

BasicMarker

org/slf4j/helpers/BasicMarker.java

public class BasicMarker implements Marker {

    private static final long serialVersionUID = -2849567615646933777L;
    private final String name;
    private final List referenceList = new CopyOnWriteArrayList<>();

    BasicMarker(String name) {
        if (name == null) {
            throw new IllegalArgumentException("A marker name cannot be null");
        }
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void add(Marker reference) {
        if (reference == null) {
            throw new IllegalArgumentException("A null value cannot be added to a Marker as reference.");
        }

        // no point in adding the reference multiple times
        if (this.contains(reference)) {
            return;

        } else if (reference.contains(this)) { // avoid recursion
            // a potential reference should not hold its future "parent" as a reference
            return;
        } else {
            referenceList.add(reference);
        }
    }

    public boolean hasReferences() {
        return (referenceList.size() > 0);
    }

    @Deprecated
    public boolean hasChildren() {
        return hasReferences();
    }

    public Iterator iterator() {
        return referenceList.iterator();
    }

    public boolean remove(Marker referenceToRemove) {
        return referenceList.remove(referenceToRemove);
    }

    public boolean contains(Marker other) {
        if (other == null) {
            throw new IllegalArgumentException("Other cannot be null");
        }

        if (this.equals(other)) {
            return true;
        }

        if (hasReferences()) {
            for (Marker ref : referenceList) {
                if (ref.contains(other)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * This method is mainly used with Expression Evaluators.
     */
    public boolean contains(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Other cannot be null");
        }

        if (this.name.equals(name)) {
            return true;
        }

        if (hasReferences()) {
            for (Marker ref : referenceList) {
                if (ref.contains(name)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static final String OPEN = "[ ";
    private static final String CLOSE = " ]";
    private static final String SEP = ", ";

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof Marker))
            return false;

        final Marker other = (Marker) obj;
        return name.equals(other.getName());
    }

    public int hashCode() {
        return name.hashCode();
    }

    public String toString() {
        if (!this.hasReferences()) {
            return this.getName();
        }
        Iterator it = this.iterator();
        Marker reference;
        StringBuilder sb = new StringBuilder(this.getName());
        sb.append(' ').append(OPEN);
        while (it.hasNext()) {
            reference = it.next();
            sb.append(reference.getName());
            if (it.hasNext()) {
                sb.append(SEP);
            }
        }
        sb.append(CLOSE);

        return sb.toString();
    }
}

BasicMarker实现了Marker接口,它定义了一个CopyOnWriteArrayList类型的referenceList

示例

配置





  
    username
    sebastien
    ACCEPT
  

  
    billing
    DENY
  

  
    
      %date [%thread] %-5level %logger - %msg%n
    
  

  
    
  

使用

Marker billing = MarkerFactory.getMarker("billing");
logger.error(billing, "billing statement {}", i);

小结

logback提供了MarkerFilter来支持slf4j的Marker,可以通过MarkerFactory.getMarker获取marker,然后在logger中使用,而配置文件可以配置MarkerFilter,设置指定的marker的onMatch或者onMismatch行为。

你可能感兴趣的:(logback,开发语言)