⾃定义log4j的Appender
编写⾃定义appender 的步骤
1. 扩展 AppenderSkeleton 抽象类。如果是通过流⽅式实现读写数据的话,⾃定⼀定appender可以从WriterAppender继承,这样只需要把我们⾃⼰的OutputStream连接到WriterAppender.qw上就可以了。更⽅便快捷。
2. 指定您的 appender 是否需要 layout。这个由requiresLayout()⽅法确定。
3. 如果某些属性必须同时激活,则应该在 activateOptions() ⽅法内完成。该⽅法上在Appender的构造函数之后被调⽤的。
4. 实现 close() ⽅法。它必须把 closed 字段的值设置为 true 。记得释放所有资源。
5. 可选地指定要使⽤的默认 ErrorHandler 对象。系统默认为OnlyOnceErrorHandler,它发送出第⼀个错误的消息并忽略其余的所有错误,错误消息将输出到 。
6. 编写 append() ⽅法的代码。这个⽅法负责附加⽇志记录事件,并在错误发⽣时负责调⽤错误处理程序。我们主要的⽇志记录等处理任务实际上是在该append()⽅法内完成的。
请看程序。
由测试程序:Log4jTest.java、
⾃定义的Appdender:UDPAppender
配置⽂件:log4j.properties
三个源⽂件组成。
测试程序:Log4jTest.java
package zieckey.study.log4j;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Log4jTest {
// 获取⽇志记录器
static Logger logger = Logger(Name());
Log4jTest() {
// 读取使⽤Java属性⽂件编写的配置⽂件
logger.debug( "Read config file." );
}
public static void printLog() {
logger.debug("Log4jTest-->>debug");
logger.info("Log4jTest-->>info");
logger.warn("Log4jTest-->>warn");
<("Log4jTest-->>error");
}
public static void main(String[] args) {
Log4jTest.printLog();
new Log4jTest();
}
}
⾃定义的Appdender:UDPAppender
4j;
import java.io.IOException;
import java.DatagramPacket;
import java.DatagramSocket;
import java.InetAddress;
import java.SocketException;
import java.UnknownHostException;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
public class UDPAppender extends AppenderSkeleton {
static private int bufferSize = 8 * 1024;
private byte data[];
private String remoteHost = "localhost";
private int port = 5000;
private InetAddress address = null;
private DatagramSocket dataSocket = null;
log4j2 配置详解propertiesprivate DatagramPacket dataPacket = null;
public UDPAppender() {
// LogLog.setInternalDebugging(true);
// LogLog.setQuietMode(false);
// LogLog.debug("default constructor.");
}
private void init() {
try {
dataSocket = new DatagramSocket(this.port + 1);
address = ByName(remoteHost);
} catch (SocketException e) {
LogLog.Message());
} catch (UnknownHostException e) {
LogLog.Message());
}
data = new byte[bufferSize];
if (this.layout == null) {
LogLog.debug("The layout is we set it.");
String pattern = "%-4r %-5p %d{yyyy-MM-dd HH:mm:ss} %c %m%n";
this.setLayout(new org.apache.log4j.PatternLayout(pattern));
}
}
@Override
protected void append(LoggingEvent event) {
try {
String msg = "send data: "
+ Layout().format(event);
data = Bytes();
dataPacket = new DatagramPacket(data, data.length, address, port);
dataSocket.send(dataPacket);
} catch (SocketException se) {
se.printStackTrace();
} catch (IOException ie) {
ie.printStackTrace();
}
}
/**
* Derived appenders should override this method if option structure
* requires it.
*/
public void activateOptions() {
init();
}
@Override
public void close() {
if (closed)
return;
if (!dataSocket.isClosed()) {
dataSocket.close();
}
closed = true;
}
@Override
public boolean requiresLayout() {
return true;
}
/**
* The <b>RemoteHost</b> option takes a string value which should be the
* host name of the server where a {@link SocketNode} is running.
* */
public void setRemoteHost(String host) {
String val = im();
remoteHost = val;
}
/
**
* Returns value of the <b>RemoteHost</b> option.
*/
public String getRemoteHost() {
return remoteHost;
}
/**
* The <b>Port</b> option takes a positive integer representing the port
* where the server is waiting for connections.
*/
public void setPort(int port) {
this.port = port;
}
/**
* Returns value of the <b>Port</b> option.
*/
public int getPort() {
return port;
}
}
配置⽂件:log4j.properties
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ss} %c %m%n
log4j.appender.UDPAppender=zieckey.study.log4j.UDPAppender
log4j.appender.UDPAppender.RemoteHost=localhost
log4j.appender.UDPAppender.Port=4561
log4j.appender.UDPAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.UDPAppender.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ss} %c %m%n
log4j.appender.LOGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.LOGFILE.File=Log4jTest.log
log4j.appender.LOGFILE.MaxFileSize=20KB
log4j.appender.LOGFILE.MaxBackupIndex=1
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ss} %c %m%n
注意配置⽂件应该放在src⽬录下。
appender 的⽣命周期
1. appender 实例不存在,或许框架还没有配置好。
2. 框架实例化了⼀个新的 appender。这发⽣在配置器类分析配置脚本中的⼀个 appender 声明的时候。配置器类调⽤ wInstance(YourCustomAppender.class) ,这等价于动态调⽤ new YourCustomAppender() 。框架这样做是为了避免被硬编码为任何特定的 appender 名称;框架是通⽤的,适⽤于任何 appender。
< log4j.appender.file=org.apache.log4j.RollingFileAppender
3. 框架判断 appender 是否需要 layout。如果该 appender 不需要 layout,配置器就不会尝试从配置脚本中加载 layout 信息。
在Appender接⼝中,有public boolean requiresLayout() ⽅法,如果return true;则要求layout,需在配置脚本中设置layout信息
< log4j.appender.file.layout=org.apache.log4j.PatternLayout
4. Log4j 配置器调⽤ setter ⽅法。在所有属性都已设置好之后,框架就会调⽤这个⽅法。
此时对应RollingFileAppender的每个Field,需要有⼀个setter⽅法,在配置脚本中也要进⾏设置
< log4j.appender.file.File=<project>.log
对应的在RollingFileAppender中,有
public void setFile(String file) {
String val = im();
fileName = val;
}
其它的属性,如MaxFileSize ,也相同
5. 配置器调⽤ activateOptions() ⽅法。在所有属性都已设置好之后,框架就会调⽤这个⽅法。
OptionHandler 接⼝定义了activateOptions()⽅法,在全部属性设置好了之后,在该⽅法中进⾏必要的操作,如打开⽂件
e.g
if(fileName != null) {
try {
setFile(fileName, fileAppend, bufferedIO, bufferSize);
}
catch(java.io.IOException e) {
<("setFile("+fileName+","+fileAppend+") call failed.",
e, ErrorCode.FILE_OPEN_FAILURE);
}
} else {
//("File option not set for appender ["+name+"].");
LogLog.warn("File option not set for appender ["+name+"].");
LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
}
6. Appender 准备就绪。此刻,框架可以调⽤ append() ⽅法来处理⽇志记录请求。这个⽅法由 AppenderSkeleton.doAppend() ⽅法调⽤。
该⽅法为Appender中最关键的⽅法,append()中可以定义⽇志的输出,最简单的:
protected void append(LoggingEvent event){
//if layout is required
System.out.Layout().format(event));
}
7. 最后,关闭appender。当框架即将要删除您的⾃定义 appender 实例时,它会调⽤您的 appender 的 close() ⽅法。 close() 是⼀个清理⽅法,意味着您需要释放已分配的所有
资源。它是⼀个必需的⽅法,并且不接受任何参数。它必须把 closed 字段设置为 true ,并在有⼈尝试使⽤关闭的 appender 时向框架发出警报。
public void close() {
if (this.closed)
return;
this.closed = true;
}
注意:需要在主程序退出前,调⽤Root().removeAllAppender();这样才能调⽤Appender的cl
ose()⽅法.
编写⾃定义appender 的步骤
1. 扩展 AppenderSkeleton 抽象类。
2. 指定您的 appender 是否需要 layout。
3. 如果某些属性必须同时激活,则应该在 activateOptions() ⽅法内完成。
4. 实现 close() ⽅法。它必须把 closed 字段的值设置为 true 。记得释放所有资源。
5. 可选地指定要使⽤的默认 ErrorHandler 对象。系统默认为OnlyOnceErrorHandler,它发送出第⼀个错误的消息并忽略其余的所有错误,错误消息将输出到 。
6. 编写 append() ⽅法的代码。这个⽅法负责附加⽇志记录事件,并在错误发⽣时负责调⽤错误处理程序。
log4j 环境包括三个主要组件:
logger(⽇志记录器):控制要启⽤或禁⽤哪些⽇志记录语句。可以对⽇志记录器指定如下级别:ALL
、DEBUG、INFO、WARN、ERROR , FATA或OFF。
layout(布局):根据⽤户的愿望格式化⽇志记录请求。
appender:向⽬的地发送格式化的输出。
所有的 appender 都必须扩展org.apache.log4j.AppenderSkeleton类,这是⼀个抽象类,它实现了org.apache.log4j.Appender和org.apache.log4j.spi.OptionHandler接⼝。AppenderSkeleton类的
UML 类图看起来如图所⽰:
package org.apache.log4j;public interface Appender {    void addFilter(Filter newFilter);                        void clearFilters() ;                                                  void close();                                                            void doAppend(LoggingEvent even 这些⽅法处理 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 的默认错误处理策略,它发送出第⼀个错误的消息并忽略其余的所有错误。错误消息将
输出到。FallbackErrorHandler实现ErrorHandler接⼝,以便能够指定⼀个辅助的 appender。如果主 appender 失败,辅助 appender 将接管⼯作。错误消息将输出
到,然后登录到新的辅助 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。
appender 实例不存在。或许框架还没有配置好。
框架实例化了⼀个新的 appender。这发⽣在配置器类分析配置脚本中的⼀个 appender 声明的时候。配置器类调⽤wInstance(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 时向框架发出警报。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。