本文主要研究一下logback的UNDEFINED_PROPERTY
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方法
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方法
ch/qos/logback/core/subst/NodeToStringTransformer.java
public String transform() throws ScanException {
StringBuilder stringBuilder = new StringBuilder();
compileNode(node, stringBuilder, new Stack());
return stringBuilder.toString();
}
transform方法主要是执行compileNode,通过stringBuilder来收集变量值
ch/qos/logback/core/subst/NodeToStringTransformer.java
private void compileNode(Node inputNode, StringBuilder stringBuilder, Stack 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方法
ch/qos/logback/core/subst/NodeToStringTransformer.java
private void handleVariable(Node n, StringBuilder stringBuilder, Stack cycleCheckStack) throws ScanException {
// Check for recursion
if (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 valid
if (value != null) {
Node innerNode = tokenizeAndParseString(value);
compileNode(innerNode, stringBuilder, cycleCheckStack);
cycleCheckStack.pop();
return;
}
// empty default literal is a valid value
if (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)
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处理逻辑。