序
本文主要研究一下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.
*
* <p>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<String, Marker> markerMap = new ConcurrentHashMap<>();
/**
* Regular users should <em>not</em> create
* <code>BasicMarkerFactory</code> instances. <code>Marker</code>
* 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.
*
* <p>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.
* </p>
*
* @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<Marker> iterator();
/**
* Does this marker contain a reference to the 'other' markerMarker 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<Marker> 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<Marker> 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<Marker> 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
示例
配置
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
<MDCKey>username</MDCKey>
<Value>sebastien</Value>
<OnMatch>ACCEPT</OnMatch>
</turboFilter>
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
<Marker>billing</Marker>
<OnMatch>DENY</OnMatch>
</turboFilter>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date [%thread] %-5level %logger - %msg%n</Pattern>
</layout>
</appender>
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>
使用
Marker billing = MarkerFactory.getMarker("billing");
logger.error(billing, "billing statement {}", i);
小结
logback提供了MarkerFilter来支持slf4j的Marker,可以通过MarkerFactory.getMarker获取marker,然后在logger中使用,而配置文件可以配置MarkerFilter,设置指定的marker的onMatch或者onMismatch行为。