`

读logback源码系列文章(五)——Appender

阅读更多
明天要带老婆出国旅游几天,所以这段时间暂时都更新不了博客了,临走前再最后发一贴

上一篇我们说到Logger类的info()方法通过层层调用,最后委托Appender来记录日志,这篇博客我们就接着说一下,Appender组件是怎么记录日志的

实际上Appender可能是logback框架中最重要的组件之一,虽然Logger是记录日志的接口,但是如果一个Logger没有关联到任何Appender的话,那么这个Logger就无法记录任何信息。此外虽然logback提供了很多扩展点,但是在应用中,我们可能很少会扩展filter,很少扩展layout和encoder,但是我们扩展Appender的机会却是很多的

老规矩,首先上图,看一下Appender的大图景,这里要说明的是,实现Appender接口有2个base类,一个是AppenderBase,另一个是UnsynchronizedAppenderBase,这2个类非常接近,80%以上的代码都是相同的。如果我们自己要自定义Appender的话,只要写一个类继承自这2个base类就好



首先是有一个Appender接口,然后如上文所说,UnsynchronizedAppenderBase类实现了这个接口,但是它本身是一个抽象类,需要继承它才能得到真正的实现类。Appender接口继承了FilterAttachable接口,而UnsynchronizedAppenderBase类持有一个FilterAttachableImpl类,委托这个类来实现FilterAttachable接口里定义的方法

然后OutputStreamAppender是继承自UnsynchronizedAppenderBase的Appender实现类,虽然它已经不是抽象类了,但是实际也是不能直接使用的,它的实现类就是最常见的ConsoleAppender和FileAppender

只要自己使用过logback的朋友都知道,appender元素下面还需要配置encoder元素,这里的Encoder接口就是对应这个encoder元素的,因为其实Appender组件还不是最终实际记录日志信息的组件,它要委托encoder组件来完成LoggingEvent的格式化和记录

介绍完了大体的结构,我们接下来就看看,从Appender接口的doAppend()方法,是怎么一步步地最终记录日志的

首先是UnsynchronizedAppenderBase里面的doAppend()方法,它主要是记录了Status状态,然后检查Appender上的Filter是否满足过滤条件,最后再调用实现子类的appender()方法。很眼熟是吗,这里用到了一个设计模式——模板方法
public void doAppend(E eventObject) {
    // WARNING: The guard check MUST be the first statement in the
    // doAppend() method.
      
    // prevent re-entry.
    if (Boolean.TRUE.equals(guard.get())) {
      return;
    }

    try {
      guard.set(Boolean.TRUE);

      if (!this.started) {
        if (statusRepeatCount++ < ALLOWED_REPEATS) {
          addStatus(new WarnStatus(
              "Attempted to append to non started appender [" + name + "].",
              this));
        }
        return;
      }

      if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
        return;
      }

      // ok, we now invoke derived class' implementation of append
      this.append(eventObject);

    } catch (Exception e) {
      if (exceptionCount++ < ALLOWED_REPEATS) {
        addError("Appender [" + name + "] failed to append.", e);
      }
    } finally {
      guard.set(Boolean.FALSE);
    }
  }

  abstract protected void append(E eventObject);

上面的代码非常简单,就不用说了,我们就直接看看实现类的append()方法是怎么实现的,这里我们选择OutputStreamAppender实现类
@Override
  protected void append(E eventObject) {
    if (!isStarted()) {
      return;
    }

    subAppend(eventObject);
  }

首先检查一下这个Appender是否已经启动,如果没启动就直接返回,如果已经启动,则又进入一个subAppend()方法
/**
   * Actual writing occurs here.
   * <p>
   * Most subclasses of <code>WriterAppender</code> will need to override this
   * method.
   * 
   * @since 0.9.0
   */
  protected void subAppend(E event) {
    if (!isStarted()) {
      return;
    }
    try {
      // this step avoids LBCLASSIC-139
      if (event instanceof DeferredProcessingAware) {
        ((DeferredProcessingAware) event).prepareForDeferredProcessing();
      }
      // the synchronization prevents the OutputStream from being closed while we
      // are writing. It also prevents multiple thread from entering the same
      // converter. Converters assume that they are in a synchronized block.
      synchronized (lock) {
        writeOut(event);
      }
    } catch (IOException ioe) {
      // as soon as an exception occurs, move to non-started state
      // and add a single ErrorStatus to the SM.
      this.started = false;
      addStatus(new ErrorStatus("IO failure in appender", this, ioe));
    }
  }

这个方法居然什么事也不干。。做了一些检查以后,又进入writeOut()方法。。。
protected void writeOut(E event) throws IOException {
    this.encoder.doEncode(event);
  }

writeOut()方法委托配置给它的Encoder组件来记录
public void doEncode(E event) throws IOException {
    String txt = layout.doLayout(event);
    outputStream.write(convertToBytes(txt));
    outputStream.flush();
  }

到这里,终于完了。Encoder组件又委托其Layout组件来将LoggingEvent进行格式化,返回一个String,然后通过OutputStream.write()方法,把格式化之后的日志信息写到目的地

耐心看到这里的朋友,可能已经有点被绕晕了,怎么Appender需要这么麻烦吗?其实我们这里说的只是ConsoleAppender的doAppend()全流程,并不是所有Appender都这么复杂的,当然也有一些更复杂的。。

下面看一个简单的Appender,就是我自己写的MyAppender
public class MyAppender extends AppenderBase<LoggingEvent> {

	@Override
	protected void append(LoggingEvent eventObject) {
		System.out.println(eventObject.getMessage());
	}

}

好吧,非常简单是不是,如果把这个Appender配置到logback.xml中,那么当Logger.info()调用的时候,就会先走进AppenderBase类的doAppend()方法里,进行Filter校验等等,然后进入MyAppender的append()方法,不做其他的操作,直接把message给打印到Console上。当然,由于这个类是极度简化的,没有Encoder和Layout,也就没办法控制输出日志的时间,也没有办法对%thread等标记进行解析处理了。但是这个类可能可以很清晰地表达出,Appender组件是怎么工作的:由AppenderBase类来调用Filter链,然后由Appender实现类来委托Encoder解析LoggingEvent,再输出到目的地

这篇博客到这里就结束了。到目前为止,5篇博客是这样的:
1、首先介绍logback怎么和slf4j对接
2、然后介绍logback的LoggerFactory,也就是LoggerContext是怎么创建的
3、接下来介绍LoggerFactory怎么创建Logger
4、然后是Logger怎么记录日志,这其中涉及了级联调用Appender,和调用TurboFilter来过滤的问题
5、本篇博客又以最常见的ConsoleAppender为例子,介绍了Appender组件怎么把日志信息输出到目的地

下一篇博客的主题有多种分支,可以再讲讲DBAppender和FileAppender是怎么记录日志的,作为这篇博客的补充,加深理解;也可以继续深入下去,说说Encoder和Layout组件;或者回头介绍一下logback是怎么初始化的

等我陪老婆旅游回来,再继续更新本系列
  • 大小: 121.1 KB
6
1
分享到:
评论

相关推荐

    springboot、logback源码解读

    Spring Boot与Logback源码解读涉及了Spring Boot框架在启动过程中如何与Logback日志系统集成,以及Logback是如何进行初始化和配置的。下面将详细解读Logback和Spring Boot的相关知识点。 ### Logback初始化过程 ...

    logback日志写logstash配置appender参考

    logback日志写logstash配置appender参考

    logback-1.1.2源码包

    **logback-1.1.2源码包详解** **一、logback介绍** Logback 是一个用于日志记录的开源框架,由 Ceki Gülcü(也创建了广泛使用的 log4j)开发。它是对 log4j 的升级,旨在提供更高的性能、更灵活的配置以及更好的...

    logback-fluentd-appender:Logback流利的Appender

    正在安装###从Maven2存储库安装jar配置pom.xml: &lt;dependencies&gt; &lt;dependency&gt; &lt;groupId&gt;com.sndyuk&lt;/groupId&gt; &lt;artifactId&gt;logback-fluentd-appender&lt;/artifactId&gt; &lt;version&gt;1.1.0&lt;/version&gt; &lt;/dependency&gt;&lt;/...

    influxdb-logback:用于 Logback 的 InfluxDB Appender

    用于 Logback 的 InfluxDB Appender 使用此 appender 将带有 logback 的消息记录到 influxdb 服务器。 如果您不知道是什么,请跳上潮流! 安装 只需将 logback-gelf 添加到您的类路径中。 或者如果您在,依赖项...

    logback-redis-appender:重新登录Redis Appender

    &lt; artifactId&gt;logback-redis-appender &lt; version&gt;1.1.6 可配置选项 与Redis相关(RedisAppender属性) 键:(必需)Redis键,用于将日志附加到 host :(可选,默认值:localhost)Redis服务器主机 端口:(可选...

    logback-elasticsearch-appender:重新登录Elasticsearch Appender

    重新登录Elasticsearch Appender 将日志事件直接从Logback发送到Elasticsearch。 日志是异步传递的(即不在主线程上),因此不会阻止程序的执行。 请注意,如果Elasticsearch关闭并且待办事项队列已满或生产者程序...

    logback-kafka-appender:适用于Apache Kafka的Logback附加程序

    logback-kafka-appender 该追加程序使您的应用程序可以将其应用程序日志直接发布到Apache Kafka。 登录不兼容警告 由于Logback Encoder API中的重大更改,您至少需要使用logback 1.2版。 完整配置示例 将logback-...

    基于Java的日志推送appender设计源码 - logback-push-appender

    本源码提供了一个基于Java编写的日志推送appender设计。项目包含49个文件,其中37个Java文件,用于实现日志推送功能。此外,还包括4个XML配置文件、2个Markdown文档、1个gitignore文件、1个jar包、1个properties文件...

    aliyun-log-logback-appender

    Logback Appender 阿里云Log Logback Appender Logback旨在作为流行的log4j项目的后继者。 您可以通过logback控制日志的目的地。 它可以是控制台,文件,GUI组件,套接字,NT事件日志,系统日志。 您也可以控制每个...

    logback日志记录写入kafka

    &lt;appender name="KAFKA" class="ch.qos.logback.classic.kafka.KafkaAppender"&gt; &lt;topic&gt;logTopic &lt;bootstrapServers&gt;localhost:9092 &lt;keySerializer&gt;org.apache.kafka.common.serialization.StringSerializer ...

    扩展logback将日志输出到Kafka实例源码

    标题"扩展logback将日志输出到Kafka实例源码"涉及的技术点主要集中在如何将Logback与Kafka集成,使得日志可以被有效地发送到Kafka集群。这个过程通常涉及到以下几个步骤: 1. **添加依赖**:首先,你需要在项目的...

    Logback Redis Appender.zip

    Logback Redis Appender用法现在位于 Maven 中央存储库&lt;dependency&gt; &lt;groupId&gt;com.cwbase&lt;/groupId&gt; &lt;artifactId&gt;logback-redis-appender&lt;/artifactId&gt; &lt;version&gt;1.1.6&lt;/version&gt;&lt;/dependency&gt;可配置选项Redis 相关...

    logback-http-appender

    《logback-http-appender在Java日志管理中的应用与实践》 在Java开发中,日志记录是一项至关重要的任务,它不仅有助于调试和追踪代码问题,还能为系统维护提供宝贵的运行信息。Logback作为一款高效的日志框架,以其...

    logback-async-appender:Logback示例代码,以显示logback JMS异步追加器

    "Logback-async-appender" 指的是 Logback 框架中的异步Appender功能,这是日志记录的一种优化策略,它允许日志事件通过消息队列(如JMS,Java消息服务)以异步方式处理,从而提高应用性能,因为日志处理不会阻塞...

    logback-websocket-appender

    logback-websocket-appender appender 通过 websocket 进行 logback。用法登录文件&lt; appender xss=removed xss=removed&gt; &lt; serverUri&gt;ws://[host]:[port]/&lt;/ serverUri&gt;&lt;/ appender&gt;

    logback+slf4j使用

    **自定义Appender** 是Logback的核心组件之一,Appender负责将日志事件写入到特定的目的地,比如控制台、文件、数据库等。自定义Appender允许开发者根据特定需求定制日志输出的方式,例如发送邮件、写入特殊格式的...

    logback日志配置demo

    接下来,我们关注 logback 的核心配置文件 —— `logback.xml`。这个文件通常放在 `src/main/resources` 目录下,因为这是 Maven 默认的资源目录。`logback.xml` 定义了日志记录的行为,如级别(TRACE, DEBUG, INFO,...

    logback jms源码分析加用法小结

    《logback jms源码分析及用法小结》 在Java世界中,日志管理是每个项目不可或缺的一部分。Logback作为一款强大的日志框架,因其高效、灵活和可配置性而深受开发者喜爱。本文将重点探讨Logback如何集成JMS(Java ...

    kinesis-logback-appender:LOGBack Appender用于将数据写入Kinesis Stream

    适用于Amazon Kinesis的LOGBack Appender 这是LOGBack的的实现。 支持Kinesis和Kinesis Firehose流。 样本配置 &lt; appender xss=removed xss=removed&gt; &lt; bufferSize&gt;1000 &lt; threadCount&gt;20 &lt; endpoint&gt;kinesis...

Global site tag (gtag.js) - Google Analytics