这个地方,在看公司的源代码的时候,写的知识点;
现在再看,竟然不是太懂,重新写一份新的文档,外加示例说明。
一:说明
1.log4j 环境的三个主要组件:
- logger(日志记录器):控制要启用或禁用哪些日志记录语句。可以对日志记录器指定如下级别:
ALL、DEBUG、INFO、WARN、ERROR,FATA或OFF。 - layout(布局):根据用户的愿望格式化日志记录请求。
- appender:向目的地发送格式化的输出。
2.理解 appender
log4j 框架允许向任何日志记录器附加多个 appender。
可以在任何时候对某个日子记录器添加(或删除)appender。附随 log4j 分发的 appender 有多个,包括:
ConsoleAppenderFileAppenderSMTPAppenderJDBCAppenderJMSAppenderNTEventLogAppenderSyslogAppender
也可以创建自己的自定义 appender。
3.工作原理
所有的 appender 都必须扩展 org.apache.log4j.AppenderSkeleton 类。
这是一个抽象类,它实现了 org.apache.log4j.Appender 和 org.apache.log4j.spi.OptionHandler 接口。

这是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.生命周期图

3.书写Appender的步骤
扩展 AppenderSkeleton 抽象类。
指定您的 appender 是否需要 layout。
如果某些属性必须同时激活,则应该在 activateOptions() 方法内完成。
实现 close() 方法。它必须把 closed 字段的值设置为 true 。记得释放所有资源。
可选地指定要使用的默认 ErrorHandler 对象。
编写 append() 方法的代码。这个方法负责附加日志记录事件,并在错误发生时负责调用错误处理程序。
4.log4j执行顺序

五:小示例
1.程序结构
感觉使用maven管理jar比较方便,这里就使用maven项目

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.效果
