配置使用
// 自动配置模板
...
<Property name="layout">%d %p %X{traceId} [%t] %c{10}:%M:%L %m%n</Property>
...
<PatternLayout pattern="${layout}"/>
...
// 具体项目覆盖配置的格式
<Property name="layout">%d %p [%t] %c{1.}:%M:%L %X{myTraceId} %m%n</Property>
MDC.put("myTraceId", myTraceId);
try {
...
} catch (Exception e) {
...
} finally {
MDC.clear();
}
日志输出效果发现是直接打印了myTraceId所对应的的值,而我们期望是这样的格式{myTraceId=123}。
原因分析
- 查看格式化的实现类PatternLayout,内部通过PatternSelector匹配选择器根据Pattern匹配选择对应的转换器进行格式化
- 默认使用MarkerPatternSelector实现,选择器构造器中解析获取各个匹配模式对应的格式化实现列表PatternFormatter
- PatternFormatter实现的实例属性LogEventPatternConverter抽象类对具体的日志内容进行格式化转换,查看其实现类
- 直接查看MdcPatternConverter实现
- 构造器中按照逗号“,”切分MDC的key配置
// options, 对应配置中的key列表
private MdcPatternConverter(final String[] options) {
super(options != null && options.length > 0 ? "MDC{" + options[0] + '}' : "MDC", "mdc");
if (options != null && options.length > 0) {
full = false;
if (options[0].indexOf(',') > 0) {
// 按照逗号切分key
keys = options[0].split(",");
for (int i = 0; i < keys.length; i++) {
keys[i] = keys[i].trim();
}
key = null;
} else {
keys = null;
key = options[0];
}
} else {
full = true;
key = null;
keys = null;
}
}
// 格式化
public void format(final LogEvent event, final StringBuilder toAppendTo) {
final ReadOnlyStringMap contextData = event.getContextData();
// if there is no additional options, we output every single
// Key/Value pair for the MDC in a similar format to Hashtable.toString()
// 如果没有附加的属性,我们输出每一个单独的MDC配置的key/value对,类似与Hashtable.toString()的格式
if (full) {
if (contextData == null || contextData.size() == 0) {
toAppendTo.append("{}");
return;
}
appendFully(contextData, toAppendTo);
} else {
if (keys != null) {
if (contextData == null || contextData.size() == 0) {
toAppendTo.append("{}");
return;
}
// 存在附加属性配置
appendSelectedKeys(keys, contextData, toAppendTo);
} else if (contextData != null){
// otherwise they just want a single key output
final Object value = contextData.getValue(key);
if (value != null) {
StringBuilders.appendValue(toAppendTo, value);
}
}
}
}
我们配置了%X扩展即存在附加属性配置
// 按照配置的MDC keys输出,输出格式为{key=value,key2=value2}
private static void appendSelectedKeys(final String[] keys, final ReadOnlyStringMap contextData, final StringBuilder sb) {
// Print all the keys in the array that have a value.
final int start = sb.length();
sb.append('{');
for (int i = 0; i < keys.length; i++) {
final String theKey = keys[i];
final Object value = contextData.getValue(theKey);
if (value != null) { // !contextData.containskey(theKey)
if (sb.length() - start > 1) {
sb.append(", ");
}
sb.append(theKey).append('=');
StringBuilders.appendValue(sb, value);
}
}
sb.append('}');
}
问题定位后修改配置即可,修改配置后验证格式符合我们的期望
<Property name="layout">%d %p [%t] %c{1.}:%M:%L %X{myTraceId,} %m%n</Property>
总结
MDC配置的key,日志会按照逗号切分出keys列表,如果keys列表小于等于1则直接输出一个单独的value值。如果大于1则按照map的格式输出,即:{key1=value1,key2=value2}