天下之事常成于困约,而败于奢靡。这篇文章主要讲述logback源码阅读-Appender相关的知识,希望能为你提供帮助。
前面我们看到 最终logger输出是委托给了appender 如果没有配置appender是不会输出的
示例配置
< ?xml version="1.0" encoding="UTF-8"?> < configuration> < property name="CHARSET" value="https://www.songbingjia.com/android/UTF-8"/> < !--为了防止进程退出时,内存中的数据丢失,请加上此选项--> < shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/> < appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> < Encoder> < pattern> < pattern> |%p|%d{yyyy-MM-dd HH:mm:ss.SSS}|%t|%logger{10}:%line%n%m%n%n< /pattern> < /pattern> < charset> ${CHARSET}< /charset> < /Encoder> < /appender> < appender name="ASYNC_STDOUT" class="ch.qos.logback.classic.AsyncAppender"> < filter class="ch.qos.logback.classic.filter.ThresholdFilter"> < level> info< /level> < /filter> < appender-ref ref="STDOUT"/> < !--最终异步委托给consoleAppender--> < includeCallerData> true< /includeCallerData> < /appender> < !--给root关联appender 默认我们所有logger都没有appender 前面看到是递归网上找父类输出 都是父类统一输出--> < root level="info"> < appender-ref ref="ASYNC_STDOUT"/> < /root> < /configuration>
文章图片
默认的appender实现我们可以根据需求选择以下默认的实现 如果没有合适的需要扩展再参考下面的相关类扩展
文章图片
下面我们举例看其中 一个如果我们有定制化需求可以参考实现定制
AsyncAppender 类图
文章图片
我们需要自定义appender只需要继承UnsynchronizedAppenderBase就行了
startch.qos.logback.core.AsyncAppenderBase#start
调用时机在解析标签处 分别调用start 和end
private AsyncAppenderBase< E> .Worker worker = new AsyncAppenderBase.Worker();
/** * 启动worker */ public void start() { //防止重复启用 if (!this.isStarted()) { //AsyncAppenderBase.AppenderAttachableImpl的数量如果美而有保存 if (this.appenderCount == 0) { this.addError("No attached appenders found."); } else if (this.queueSize < 1) { this.addError("Invalid queue size [" + this.queueSize + "]"); } else { //初始化一个线程安全的数组阻塞队列 ququeSize默认为256 this.blockingQueue = new ArrayBlockingQueue(this.queueSize); if (this.discardingThreshold == -1) { this.discardingThreshold = this.queueSize / 5; }this.addInfo("Setting discardingThreshold to " + this.discardingThreshold); this.worker.setDaemon(true); this.worker.setName("AsyncAppender-Worker-" + this.getName()); //重写了父类的start 所以保证不破坏父类逻辑 所以调用父类start super.start(); //< 1> 启动一个worker AsyncAppenderBase< E> .Worker worker = new AsyncAppenderBase.Worker(); 内部类 消费队列数据 委托给当前类的 AppenderAttachableImpl< E> aai = new AppenderAttachableImpl(); this.worker.start(); //这里是调用线程的start } } }
start调用点在解析完append end标签之后 后面会讲
ch.qos.logback.core.joran.action.AppenderAction#end
public void end(InterpretationContext ec, String name) { if (!this.inError) { //如果实现了LifeCycle 接口则调用start方法 if (this.appender instanceof LifeCycle) { this.appender.start(); }Object o = ec.peekObject(); if (o != this.appender) { this.addWarn("The object at the of the stack is not the appender named [" + this.appender.getName() + "] pushed earlier."); } else { ec.popObject(); }} }
< 1>
class Worker extends Thread { Worker() { }public void run() { AsyncAppenderBase< E> parent = AsyncAppenderBase.this; AppenderAttachableImpl aai = parent.aai; //判断主类对象是否started while (parent.isStarted()) { try { //循环消费 E e = parent.blockingQueue.take(); //< 3> 这里是委托给前面 < appender-ref ref="STDOUT"/> 配置最终还是Console 这里我们可以配置的appender 上面用例配置的STDOUT aai.appendLoopOnAppenders(e); } catch (InterruptedException var5) { break; } } AsyncAppenderBase.this.addInfo("Worker thread will flush remaining events before exiting. "); //以下是当started关闭 不接受消息 但是还是要消费完 Iterator i$ = parent.blockingQueue.iterator(); while (i$.hasNext()) { E ex = i$.next(); aai.appendLoopOnAppenders(ex); parent.blockingQueue.remove(ex); } //相关委托的appender started也标识为false aai.detachAndStopAllAppenders(); } }
可以看到aynycAppender最终没有做实际的事情最终还是委托给了 AppenderAttachableImpl 对应上面配置内部封装的就是ConsoleAppender
AppenderAttachableImpl < 3> appendLoopOnAppendersch.qos.logback.core.spi.AppenderAttachableImpl#appendLoopOnAppenders
public int appendLoopOnAppenders(E e) { int size = 0; //获得容器内的所有appender Appender< E> [] appenderArray = (Appender[])this.appenderList.asTypedArray(); int len = appenderArray.length; for(int i = 0; i < len; ++i) { //< 4> 逐个调用doAppend appenderArray[i].doAppend(e); ++size; }return size; }
UnsynchronizedAppenderBase < 4> doAppendch.qos.logback.core.spi.AppenderAttachableImpl#appendLoopOnAppenders
->
ch.qos.logback.core.UnsynchronizedAppenderBase#doAppend
private ThreadLocal< Boolean> guard = new ThreadLocal(); public void doAppend(E eventObject) { //防止一个线程同时进入多个吧。。。 if (!Boolean.TRUE.equals(this.guard.get())) { try { this.guard.set(Boolean.TRUE); //必须为started状态 if (this.started) { //appender的过滤器 参考ThresholdFilter 实现 可以对消息搜集做过滤 if (this.getFilterChainDecision(eventObject) == FilterReply.DENY) { return; } //< 5> 模板模式化 由子类实现 this.append(eventObject); return; }if (this.statusRepeatCount++ < 3) { this.addStatus(new WarnStatus("Attempted to append to non started appender [" + this.name + "].", this)); } } catch (Exception var6) { if (this.exceptionCount++ < 3) { this.addError("Appender [" + this.name + "] failed to append.", var6); }return; } finally { this.guard.set(Boolean.FALSE); }} }protected abstract void append(E var1);
OutputStreamAppender < 5> appendch.qos.logback.core.spi.AppenderAttachableImpl#appendLoopOnAppenders
->
ch.qos.logback.core.UnsynchronizedAppenderBase#doAppend
->
ch.qos.logback.core.OutputStreamAppender#append
protected void append(E eventObject) { if (this.isStarted()) { this.subAppend(eventObject); } }
ch.qos.logback.core.OutputStreamAppender#subAppend
protected void subAppend(E event) { if (this.isStarted()) { try { if (event instanceof DeferredProcessingAware) { ((DeferredProcessingAware)event).prepareForDeferredProcessing(); }
//encoder具体查看https://www.cnblogs.com/LQBlog/p/12164918.html#autoid-2-0-0 byte[] byteArray = this.encoder.encode(event); this.writeBytes(byteArray); } catch (IOException var3) { this.started = false; this.addStatus(new ErrorStatus("IO failure in appender", this, var3)); }} }
private void writeBytes(byte[] byteArray) throws IOException { if (byteArray != null & & byteArray.length != 0) { this.lock.lock(); try { //System.out进行输出 this.outputStream.write(byteArray); if (this.immediateFlush) { this.outputStream.flush(); } } finally { this.lock.unlock(); }} }
总结1.Logger必须和appender关联才可以产生作用
【logback源码阅读-Appender】2.我们可以为Appender配置Filter做定制的过滤
推荐阅读
- Android中点击按钮获取string.xml中内容并弹窗提示
- 利用kali生成木马远程控制安卓手机
- uniapp中小程序的授权操作
- vant weapp tab切换 结构隐藏 下方高亮线消失解决方案
- class path resource [applicationContext.xml] cannot be opened because it does not exist
- Android中通过数组资源文件xml与适配器两种方式给ListView列表视图设置数据源
- flower 指定app
- WPF学习第七章 WrapPanel和DockPanel面板
- Flutter mac上打包安卓APK遇到的坑