序
本文主要研究一下logback的UNDEFINED_PROPERTY
substVars
ch/qos/logback/core/util/OptionHelper.java
public static String substVars(String input, PropertyContainer pc0, PropertyContainer pc1) {try {return NodeToStringTransformer.substituteVariable(input, pc0, pc1);} catch (ScanException e) {throw new IllegalArgumentException("Failed to parse input [" + input + "]", e);}}
OptionHelper提供了substVars方法,它执行NodeToStringTransformer的substituteVariable方法
substituteVariable
ch/qos/logback/core/subst/NodeToStringTransformer.java
public static String substituteVariable(String input, PropertyContainer pc0, PropertyContainer pc1) throws ScanException {Node node = tokenizeAndParseString(input);NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, pc0, pc1);return nodeToStringTransformer.transform();}
substituteVariable方法则先根据input解析node,再创建NodeToStringTransformer,执行其transform方法
transform
ch/qos/logback/core/subst/NodeToStringTransformer.java
public String transform() throws ScanException {StringBuilder stringBuilder = new StringBuilder();compileNode(node, stringBuilder, new Stack<Node>());return stringBuilder.toString();}
transform方法主要是执行compileNode,通过stringBuilder来收集变量值
compileNode
ch/qos/logback/core/subst/NodeToStringTransformer.java
private void compileNode(Node inputNode, StringBuilder stringBuilder, Stack<Node> cycleCheckStack) throws ScanException {Node n = inputNode;while (n != null) {switch (n.type) {case LITERAL:handleLiteral(n, stringBuilder);break;case VARIABLE:handleVariable(n, stringBuilder, cycleCheckStack);break;}n = n.next;}}
compileNode方法针对VARIABLE类型的执行handleVariable方法
handleVariable
ch/qos/logback/core/subst/NodeToStringTransformer.java
private void handleVariable(Node n, StringBuilder stringBuilder, Stack<Node> cycleCheckStack) throws ScanException {// Check for recursionif (haveVisitedNodeAlready(n, cycleCheckStack)) {cycleCheckStack.push(n);String error = constructRecursionErrorMessage(cycleCheckStack);throw new IllegalArgumentException(error);}cycleCheckStack.push(n);StringBuilder keyBuffer = new StringBuilder();Node payload = (Node) n.payload;compileNode(payload, keyBuffer, cycleCheckStack);String key = keyBuffer.toString();String value = lookupKey(key);// empty values are considered validif (value != null) {Node innerNode = tokenizeAndParseString(value);compileNode(innerNode, stringBuilder, cycleCheckStack);cycleCheckStack.pop();return;}// empty default literal is a valid valueif (n.defaultPart == null) {stringBuilder.append(key + CoreConstants.UNDEFINED_PROPERTY_SUFFIX);cycleCheckStack.pop();return;}Node defaultPart = (Node) n.defaultPart;StringBuilder defaultPartBuffer = new StringBuilder();compileNode(defaultPart, defaultPartBuffer, cycleCheckStack);cycleCheckStack.pop();String defaultVal = defaultPartBuffer.toString();stringBuilder.append(defaultVal);}
handleVariable方法对于value为null,且defaultValue也为null的设置了默认值为
key + CoreConstants.UNDEFINED_PROPERTY_SUFFIX)
UNDEFINED_PROPERTY_SUFFIX
public class CoreConstants {//......public static final String UNDEFINED_PROPERTY_SUFFIX = "_IS_UNDEFINED";}
UNDEFINED_PROPERTY_SUFFIX的值为
_IS_UNDEFINED
小结
logback通过NodeToStringTransformer的handleVariable来获取变量值,若该value为null,且defaultValue也为null的设置了默认值为key + CoreConstants.UNDEFINED_PROPERTY_SUFFIX)
,即key_IS_UNDEFINED
,注意这里如果defaultValue不为null则不走IS_UNDEFINED的逻辑,即空字符串也是可以的。对于自定义appender需要注意一下appender属性的value处理逻辑。