childrenList
0 = {Logger@13299} “Logger[SYSLOGGER]”
1 = {Logger@6444}Logger[BASELOGGER]"
2 = {Logger@13300} “Logger[BUSILOGGER]”
3 = {Logger@13301} “Logger[MONLOGGER]”
4 = {Logger@13302} “Logger[org]”
5 = {Logger@13303} “Logger[com]”
6 = {Logger@13304} “Logger[cn]”
appenderList
2 = {Logger@13300} “Logger[BUSILOGGER]” 下面有个appenderList,appenderList下面有2个输出目的地。
0 = {AsyncAppender@13142} ch.qos.logback.classic.AsyncAppender[ASYNC_SYSFILE]"
1 = {ConsoleAppender@10175} “ch.qos.logback.core.ConsoleAppender[STDOUT]”
这2个目的地就是对应我们的Logback配置文件。一个是控制台,一个是日志文件。
public int appendLoopOnAppenders(E e) {
int size = 0;
final Appender<E>[] appenderArray = appenderList.asTypedArray();
final int len = appenderArray.length;
for (int i = 0; i < len; i++) {
appenderArray[i].doAppend(e); 通过doAppend方法对2个目的地追加要打印的日志。
size++;
}
return size;
}
在执行subAppend方法的时候,会对日志按照指定的输出logback.xml配置的格式转换成byte
protected void subAppend(E event) {
byte[] byteArray = this.encoder.encode(event);
writeBytes(byteArray); //滚动写入到对应的目的地。
}
public byte[] encode(E event) {
String txt = layout.doLayout(event);
return convertToBytes(txt);
}
layout就是日志格式对象,该对象包含很多属性,日志格式属性对象比如下面这种
[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%cn] [%X{tid}] [%ip] [%ALogger] [%-5level] [%thread] [%AClass{36}:%ALine] [%X{pid}] [%X{sid}] #%msg#%n
最终会调用下面方法,来遍历虽有的处理类,将日志格式中的变量替换,这里就要重点介绍处理类了。
protected String writeLoopOnConverters(E event) {
StringBuilder strBuilder = new StringBuilder(INTIAL_STRING_BUILDER_SIZE); 定义字符串,通过append的方式追加到尾部。
Converter<E> c = head; //head表示第一个处理类,head有个方法getNext(),就是拿到第二个处理类,每个处理类都有next,直到最后一个next为null
while (c != null) {
c.write(strBuilder, event); //调用【处理类】的write方法,把要替换的日志写到strBuilder里面去。
c = c.getNext();
}
return strBuilder.toString();
}
日志格式里面有很多变量,都需要不同的【处理类】来替换,那么每个变量都有对应的处理类,比如上面我们定义了变量tid、ip、ALine、pid这是我们自己定义的变量,那么我们就要往里面添加对应的处理类,比如处理Ip的处理类IpConverter.class,处理ALine变量的类ALineOfCallerConverter.class,这都是处理类,都是自己定义的。下面会介绍如何定义这些处理类。
定义日志输出格式:
方式一:
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %npattern>
encoder>
方式二:
<encoder class="cn.com.xxx.component.logback.encoder.PatternLayoutEncoder">encoder>
PatternLayoutEncoder是我们自己定义的类,继承了PatternLayoutEncoderBase接口
public class PatternLayoutEncoder extends PatternLayoutEncoderBase<ILoggingEvent> {
//定义输出格式
private static String PATTERN_SUFFIX = "[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%cn] [%X{tid}] [%ip] [%ALogger] [%-5level] [%thread] [%AClass{36}:%Line] [%X{pid}] [%X{sid}] #%msg#%n";
public PatternLayoutEncoder() {
}
//重写了父类start方法
public void start() {
APatternLayout patternLayout = new APatternLayout();
patternLayout.setContext(this.context);
patternLayout.setPattern(PATTERN_SUFFIX);
patternLayout.setOutputPatternAsHeader(this.outputPatternAsHeader);
patternLayout.start();
this.layout = patternLayout;
super.start();
}
}
上面有个APatternLayout,主要是完成往【处理类Map】里面添加我们自定义的【处理类】
public class APatternLayout extends PatternLayout {
public APatternLayout() {
}
static {
//defaultConverterMap处理类的Map
defaultConverterMap.put("ip", IpConverter.class.getName());
defaultConverterMap.put("AClass", AClassConverter.class.getName());
defaultConverterMap.put("ALine", ALineOfCallerConverter.class.getName());
defaultConverterMap.put("ALogger", ALoggerPatternConverter.class.getName());
defaultConverterMap.put("X", LogbackMDCPatternConverter.class.getName());
}
}
比如IpConverter.class处理类,需要继承ClassicConverter类,然后重写对应的convert方法。
public class IpConverter extends ClassicConverter {
public IpConverter() {
}
public String convert(ILoggingEvent event) {
return getLocalHostIP();
}
private static String getLocalHostIP() {
try {
InetAddress addr = InetAddress.getLocalHost();
return addr.getHostAddress();
} catch (UnknownHostException var1) {
return "127.0.0.1";
}
}
}