这个地方,在看公司的源代码的时候,写的知识点;

  现在再看,竟然不是太懂,重新写一份新的文档,外加示例说明。

一:说明

1.log4j 环境的三个主要组件:

logger(日志记录器)控制要启用或禁用哪些日志记录语句。可以对日志记录器指定如下级别: ALL 、DEBUG 、 INFO 、 WARN 、 ERROR , FATA或 OFF 。 
layout(布局):根据用户的愿望格式化日志记录请求。 
appender:向目的地发送格式化的输出。

2.理解 appender

  log4j 框架允许向任何日志记录器附加多个 appender。

  可以在任何时候对某个日子记录器添加(或删除)appender。附随 log4j 分发的 appender 有多个,包括:

ConsoleAppender
FileAppender
SMTPAppender
JDBCAppender
JMSAppender
NTEventLogAppender
SyslogAppender

  也可以创建自己的自定义 appender。

3.工作原理

   所有的 appender 都必须扩展 org.apache.log4j.AppenderSkeleton 类。

  这是一个抽象类,它实现了 org.apache.log4j.Appender 和 org.apache.log4j.spi.OptionHandler 接口。

  002 使用Appender扩展logger框架-风君雪科技博客

  这是AppenderSkeleton的UML类图。

二:Appender接口

1.Appender接口:

 1 import org.apache.log4j.spi.ErrorHandler;
 2 import org.apache.log4j.spi.Filter;
 3 import org.apache.log4j.spi.LoggingEvent;
 4 
 5 public interface Appender {
 6     void addFilter(Filter var1);
 7 
 8     Filter getFilter();
 9 
10     void clearFilters();
11 
12     void close();
13 
14     void doAppend(LoggingEvent var1);
15 
16     String getName();
17 
18     void setErrorHandler(ErrorHandler var1);
19 
20     ErrorHandler getErrorHandler();
21 
22     void setLayout(Layout var1);
23 
24     Layout getLayout();
25 
26     void setName(String var1);
27 
28     boolean requiresLayout();
29 }

  

2.对上文的注解说明

  这些方法处理 appender 的如下属性:

  name:  Appender 是命名的实体,因此有一个针对其名称的 setter/getter。
  layout: Appender 可以具有关联的 Layout,因此还有另一个针对 layout 的setter/getter 方法。

    注意我们说的是“可以”而不是“必须”。这是因为有些 appender 不需要 layout。

    lauout 管理格式输出――也就是说,它返回LoggingEvent 的 String 表示形式。

    另一方面, JMSAppender 发送的事件是 串行化的,因此您不需要对它附加 layout。如果自定义的 appender 不需要 layout,那么 requiresLayout() 方法必须返回 false ,以避免 log4j 抱怨说丢失了 layout 信息。
  errorHandler : 另一个 setter/getter 方法是为 ErrorHandler 而存在的。

    appender 可能把它们的错误处理委托给一个 ErrorHandler 对象――即 org.apache.log4j.spi 包中的一个接口。

    实现类有两个: OnlyOnceErrorHandler 和 FallbackErrorHandler 。

     OnlyOnceErrorHandle 实现 log4j 的默认错误处理策略,它发送出第一个错误的消息并忽略其余的所有错误。错误消息将输出到 System.err 。

    FallbackErrorHandler 实现 ErrorHandler 接口,以便能够指定一个辅助的 appender。如果主 appender 失败,辅助 appender 将接管工作。错误消息将输出到 System.err ,然后登录到新的辅助 appender。

  还有管理过滤器的其他方法(比如 ddFilter() 、 clearFilters() 和 getFilter() 方法 )。尽管 log4j 具有过滤日志请求的多种内置方法(比如知识库范围级、日志记录器级和 appender 阈值级),但它使用自定义过滤器方法的能力也是非常强大的。

    一个 appender 可以包含多个过滤器。

    自定义过滤器必须扩展 org.apache.log4j.spi.Filter 抽象类。这个抽象类要求把过滤器组织为线性链。

    对每个过滤器的 decide(LoggingEvent) 方法的调用要按照过滤器被添加到链中的顺序来进行。

    自定义过滤器基于三元逻辑。 decide() 方法必须返回 DENY 、 NEUTRAL 或者 ACCEPT 这三个整型常量值之一。

  除了 setter/getter 方法以及和过滤器相关的方法外,还有另外两个方法: close() 和 doAppend() 。 close() 方法释放 appender 中分配的任何资源,比如文件句柄、网络连接,等等。

    在编写自定义 appender 代码时,务必要实现这个方法,以便当您的 appender 关闭时,它的 closed 字段将被设置为 true 。

3.doAppend方法的源代码

 1 public synchronized void doAppend (LoggingEvent event) {
 2   if (closed) {
 3     // step 1
 4     LogLog.error("Attempted to append to closed appender [" + name + "].");
 5     return;
 6   } if ( !isAsSevereAsThreshold (event.level) ) {
 7     // step 2
 8     return;
 9   }
10   Filter f = this.headFilter;
11   // step 3
12   FILTER_LOOP:
13   while ( f != null) {
14     switch ( f .decide(event) ) {
15       case Filter.DENY: return;
16       case Filter.ACCEPT: break FILTER_LOOP;
17       case Filter.NEUTRAL: f = f.next;
18     }
19   }
20   this.append(event);
21   // step 4
22 }

   doAppend() 方法之前就提到了 append() 方法。

  它是自定义 appender 必须实现的一个抽象方法,因为框架在 doAppend() 方法内调用 append() 方法。 append() 方法是框架的钩子(hook)之一。

4.doAppender算法框架

  检查 appender 是否关闭。附加关闭的 appender 是一个编程错误。

  检查正在记录日志的事件是否处于 appender 的阈值之下。

  检查是否有过滤器附加到 appender,如果有,则拒绝请求。

  调用 appender 的 append() 方法。这个步骤被委托给每个子类。

三:OptionHandler

1.OptionHandler 接口说明

  OptionHandler 仅包含一个方法: activateOptions() 。

  这个方法在对属性调用 setter 方法之后由一个配置器类调用。

  有些属性彼此依赖,因此它们在全部加载完成之前是无法激活的,比如在 activateOptions() 方法中就是这样。

  这个方法是开发人员在 appender 变为激活和就绪之前用来执行任何必要任务的机制。

2.OptionHandler 接口

1 package org.apache.log4j.spi;
2 
3 public interface OptionHandler {
4     void activateOptions();
5 }

  

3.对上文的注解说明

  OptionHandler 仅包含一个方法: activateOptions() 。

  这个方法在对属性调用 setter 方法之后由一个配置器类调用。

  有些属性彼此依赖,因此它们在全部加载完成之前是无法激活的,比如在 activateOptions() 方法中就是这样。

  这个方法是开发人员在 appender 变为激活和就绪之前用来执行任何必要任务的机制。

四:理论总结

1.Appender生命周期

appender 实例不存在。或许框架还没有配置好。 
框架实例化了一个新的 appender。这发生在配置器类分析配置脚本中的一个 appender 声明的时候。配置器类调用 Class.newInstance(YourCustomAppender.class) ,这等价于动态调用 new YourCustomAppender() 。框架这样做是为了避免被硬编码为任何特定的 appender 名称;框架是通用的,适用于任何 appender。 
框架判断 appender 是否需要 layout。如果该 appender 不需要 layout,配置器就不会尝试从配置脚本中加载 layout 信息。 
Log4j 配置器调用 setter 方法。在所有属性都已设置好之后,框架就会调用这个方法。程序员可以在这里激活必须同时激活的属性。 
配置器调用 activateOptions() 方法。在所有属性都已设置好之后,框架就会调用这个方法。程序员可以在这里激活必须同时激活的属性。 
Appender 准备就绪。 此刻,框架可以调用 append() 方法来处理日志记录请求。这个方法由 AppenderSkeleton.doAppend() 方法调用。 
最后,关闭appender。 当框架即将要删除您的自定义 appender 实例时,它会调用您的 appender 的 close() 方法。 close() 是一个清理方法,意味着 您需要释放已分配的所有资源。它是一个必需的方法,并且不接受任何参数。它必须把 closed 字段设置为 true ,并在有人尝试使用关闭的 appender 时向框架发出警报。

2.生命周期图

  002 使用Appender扩展logger框架-风君雪科技博客

3.书写Appender的步骤

  扩展 AppenderSkeleton 抽象类。 

  指定您的 appender 是否需要 layout。

  如果某些属性必须同时激活,则应该在 activateOptions() 方法内完成。 

  实现 close() 方法。它必须把 closed 字段的值设置为 true 。记得释放所有资源。 

  可选地指定要使用的默认 ErrorHandler 对象。 

  编写 append() 方法的代码。这个方法负责附加日志记录事件,并在错误发生时负责调用错误处理程序。

4.log4j执行顺序

  002 使用Appender扩展logger框架-风君雪科技博客

五:小示例

1.程序结构

  感觉使用maven管理jar比较方便,这里就使用maven项目

  002 使用Appender扩展logger框架-风君雪科技博客

2.pom

  一直在加包,导致现在也不清楚需要多少包,以后这里再研究。

 1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 2   <modelVersion>4.0.0</modelVersion>
 3   <groupId>appender</groupId>
 4   <artifactId>jun.it</artifactId>
 5   <version>0.0.1-SNAPSHOT</version>
 6   <name>AppenderDemo</name>
 7   <dependencies>
 8       <!-- https://mvnrepository.com/artifact/log4j/log4j -->
 9     <dependency>
10         <groupId>log4j</groupId>
11         <artifactId>log4j</artifactId>
12         <version>1.2.17</version>
13     </dependency>
14     <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
15     <dependency>
16         <groupId>org.apache.logging.log4j</groupId>
17         <artifactId>log4j-core</artifactId>
18         <version>2.10.0</version>
19     </dependency>
20     <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
21     <dependency>
22         <groupId>org.apache.logging.log4j</groupId>
23         <artifactId>log4j-api</artifactId>
24         <version>2.10.0</version>
25     </dependency>
26     <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
27     <dependency>
28         <groupId>org.apache.logging.log4j</groupId>
29         <artifactId>log4j-slf4j-impl</artifactId>
30         <version>2.10.0</version>
31     </dependency>
32     <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
33     <dependency>
34         <groupId>commons-logging</groupId>
35         <artifactId>commons-logging</artifactId>
36         <version>1.2</version>
37     </dependency>
38 
39     
40         
41         
42   
43   </dependencies>
44 </project>

3.HelloAppender

 1 package com.jun.it;
 2 
 3 import org.apache.log4j.AppenderSkeleton;
 4 import org.apache.log4j.spi.LoggingEvent;
 5 
 6 public class HelloAppender extends AppenderSkeleton {
 7     // ==============参数==============
 8     private String account;
 9 
10     public String getAccount() {
11         return account;
12     }
13 
14     public void setAccount(String account) {
15         this.account = account;
16     }
17     // ================================
18     
19     public void close() {
20 
21     }
22 
23     public boolean requiresLayout() {
24         return false;
25     }
26 
27     @Override
28     protected void append(LoggingEvent event) {
29         System.out.println("Hello, " + account + " : " + event.getMessage());
30     }
31 
32 }

4.测试类

 1 package com.jun.it;
 2 
 3 import org.apache.commons.logging.Log;
 4 import org.apache.commons.logging.LogFactory;
 5 
 6 public class TestAppenderDemo {
 7 
 8     public static void main(String[] args) {
 9         Log log = LogFactory.getLog("hello");  
10         log.info("I am ready."); 
11 
12     }
13 
14 }

5.log4j.properties

1 log4j.rootLogger=INFO,hello
2 log4j.appender.hello=com.jun.it.HelloAppender
3 log4j.appender.hello.account=world
4 log4j.appender.hello.Encoding=UTF-8
5 log4j.appender.hello.Threshold=DEBUG
6 log4j.appender.hello.DatePattern=yyyy-MM-dd'.log'

6.效果

  002 使用Appender扩展logger框架-风君雪科技博客