因为有需求sentry,钉钉,kafka进行日志追踪,有错误的时候可以直接发送警报,需要与logback进行整合。
先看下Logger的构造函数,其实就是一个多叉树,头结点为root,子节点的名称分别为下一层的包名,其中LoggerContext是一个全局的logger上下文,里面装有所有logger的指针和一些系统配置如xml文件
Logger(String name, Logger parent, LoggerContext loggerContext) {
//当前这一层的包名,顶层为root
this.name = name;
//父节点,顶层为null
this.parent = parent;
//logger的执行上下文
this.loggerContext = loggerContext;
}
再来说一下AppenderAttachableImpl这个类只会在有Appender的结点进行创建,按照我们把logback.xml配置在resources目录的默认约定,我们重写的Appender只会出现在root结点的logger初始化的时候会把配置文件里的重写的Appender加载到一个对CopyOnWriteArrayList进行再次封装的COWArrayList集合,添加Appender方法简化如下:
public synchronized void addAppender(Appender<ILoggingEvent> newAppender) {
if (aai == null) {
aai = new AppenderAttachableImpl<ILoggingEvent>();
}
aai.addAppender(newAppender);
}
当遇到打印日志需求的时候,就会取当前包下的logger调用callAppenders去找到AppenderAttachableImpl如果为null就会一直去父节点找,直到找到root结点为止
public void callAppenders(ILoggingEvent event) {
int writes = 0;
for (Logger l = this; l != null; l = l.parent) {
writes += l.appendLoopOnAppenders(event);
}
}
private int appendLoopOnAppenders(ILoggingEvent event) {
if (aai != null) {
return aai.appendLoopOnAppenders(event);
} else {
return 0;
}
}
再来看看aai.appendLoopOnAppenders(event)这个方法,虽然方法名一样但是不要搞混淆了,上面那个是logger的方法,下面这个是AppenderAttachableImpl的方法
public int appendLoopOnAppenders(E e) {
int size = 0;
//获取Appender数组
final Appender<E>[] appenderArray = appenderList.asTypedArray();
final int len = appenderArray.length;
for (int i = 0; i < len; i++) {
//虽然都是实现了Appender的类,但具体实现不同
appenderArray[i].doAppend(e);
size++;
}
return size;
}
doAppend()是抽象类AppenderBase的一个方法经过一系列判断然后最终调用getFilterChainDecision()里面主要是有一堆的过滤器包括自己指定的Filter要是有其他需求也可以写一个类extends Filter自己写过滤逻辑
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
public FilterReply getFilterChainDecision(E event) {
final Filter<E>[] filterArrray = filterList.asTypedArray();
final int len = filterArrray.length;
for (int i = 0; i < len; i++) {
//一堆过滤器
final FilterReply r = filterArrray[i].decide(event);
if (r == FilterReply.DENY || r == FilterReply.ACCEPT) {
return r;
}
}
// 不符合要求
return FilterReply.NEUTRAL;
}
经过过滤器日志等级判定后最终会调用this.append(),所以我们只需要继承AppenderBase重写append()方法就可以写需要的业务逻辑,不管是钉钉、sentry、还是mq都可以按如下代码:
public class SentryAppender extends AppenderBase<ILoggingEvent> {
@Override
protected void append(ILoggingEvent iLoggingEvent) {
EventBuilder eventBuilder = createEventBuilder(iLoggingEvent);
Sentry.capture(eventBuilder);
}
filter同理
<appender name="SENTRY" class="io.sentry.logback.SentryAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
</appender>