编写自定义appender 的 步骤
扩展 AppenderSkeleton 抽象类。如果是通过流方式实现读写数据的话,自定一定appender可以从WriterAppender继承,这样只需要把我们自己的OutputStream连接到WriterAppender.qw上就可以了。更方便快捷。 指定您的 appender 是否需要 layout。这个由requiresLayout()方法确定。 如果某些属性必须同时激活,则应该在 activateOptions() 方法内完成。该方法上在Appender的构造函数之后被调用的。 实现 close() 方法。它必须把 closed 字段的值设置为 true 。记得释放所有资源。 可选地指定要使用的默认 ErrorHandler 对象。系统默认为OnlyOnceErrorHandler,它发送出第一个错误的消息并忽略其余的所有错误,错误消息将输出到 System.err。编写 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.getLogger(Log4jTest.class.getName()); Log4jTest() { // 读取使用Java属性文件编写的配置文件 logger.debug( "Read config file." ); PropertyConfigurator.configure("src/log4j.properties"); } public static void printLog() { logger.debug("Log4jTest-->>debug"); logger.info("Log4jTest-->>info"); logger.warn("Log4jTest-->>warn"); logger.error("Log4jTest-->>error"); } public static void main(String[] args) { Log4jTest.printLog(); new Log4jTest(); }}
自定义的Appdender:UDPAppender
4j;
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.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; private 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 = InetAddress.getByName(remoteHost); } catch (SocketException e) { LogLog.debug(e.getMessage()); } catch (UnknownHostException e) { LogLog.debug(e.getMessage()); } data = new byte[bufferSize]; if (this.layout == null) { LogLog.debug("The layout is not loaded... 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 = "UDP Appender...send data: " + this.getLayout().format(event); data = msg.getBytes(); 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 RemoteHost 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 = host.trim(); remoteHost = val; } /** * Returns value of the RemoteHost option. */ public String getRemoteHost() { return remoteHost; } /** * The Port 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 Port option. */ public int getPort() { return port; }}
配置文件:log4j.properties
log4j.rootLogger=DEBUG,CONSOLE,UDPAppender,LOGFILE
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目录下。
PS:网上的一篇好文 http://www.blogjava.net/dwys0343/articles/96019.html 自定义log4j的Appender appender 的生命周期
appender 实例不存在,或许框架还没有配置好。 框架实例化了一个新的 appender。这发生在配置器类分析配置脚本中的一个 appender 声明的时候。配置器类调用 Class.newInstance(YourCustomAppender.class) ,这等价于动态调用 new YourCustomAppender() 。框架这样做是为了避免被硬编码为任何特定的 appender 名称;框架是通用的,适用于任何 appender。e.g. log4j.appender.file=org.apache.log4j.RollingFileAppender
框架判断 appender 是否需要 layout。如果该 appender 不需要 layout,配置器就不会尝试从配置脚本中加载 layout 信息。在Appender接口中,有public boolean requiresLayout() 方法,如果return true;则要求layout,需在配置脚本中设置layout信息 e.g. log4j.appender.file.layout=org.apache.log4j.PatternLayout
Log4j 配置器调用 setter 方法。在所有属性都已设置好之后,框架就会调用这个方法。此时对应RollingFileAppender的每个Field,需要有一个setter方法,在配置脚本中也要进行设置 e.g. log4j.appender.file.File=.log 对应的在RollingFileAppender中,有 public void setFile(String file) { String val = file.trim(); fileName = val; } 其它的属性,如MaxFileSize ,也相同
配置器调用 activateOptions() 方法。在所有属性都已设置好之后,框架就会调用这个方法。OptionHandler 接口定义了activateOptions()方法,在全部属性设置好了之后,在该方法中进行必要的操作,如打开文件 e.g if(fileName != null) { try { setFile(fileName, fileAppend, bufferedIO, bufferSize); } catch(java.io.IOException e) { errorHandler.error("setFile("+fileName+","+fileAppend+") call failed.", e, ErrorCode.FILE_OPEN_FAILURE); } } else { //LogLog.error("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?"); }
Appender 准备就绪。 此刻,框架可以调用 append() 方法来处理日志记录请求。这个方法由 AppenderSkeleton.doAppend() 方法调用。该方法为Appender中最关键的方法,append()中可以定义日志的输出,最简单的: protected void append(LoggingEvent event){ //if layout is required System.out.println(this.getLayout().format(event)); }
最后,关闭appender。 当框架即将要删除您的自定义 appender 实例时,它会调用您的 appender 的 close() 方法。 close() 是一个清理方法,意味着 您需要释放已分配的所有资源。它是一个必需的方法,并且不接受任何参数。它必须把 closed 字段设置为 true ,并在有人尝试使用关闭的 appender 时向框架发出警报。public void close() { if (this.closed) return; this.closed = true; } 注意: 需要在主程序退出前,调用Logger.getRoot().removeAllAppender();这样才能调用Appender的close()方法.
编写自定义appender 的 步骤
扩展 AppenderSkeleton 抽象类。 指定您的 appender 是否需要 layout。 如果某些属性必须同时激活,则应该在 activateOptions() 方法内完成。 实现 close() 方法。它必须把 closed 字段的值设置为 true 。记得释放所有资源。 可选地指定要使用的默认 ErrorHandler 对象。系统默认为OnlyOnceErrorHandler,它发送出第一个错误的消息并忽略其余的所有错误,错误消息将输出到 System.err。编写 append() 方法的代码。这个方法负责附加日志记录事件,并在错误发生时负责调用错误处理程序。转载于:https://www.cnblogs.com/Dennis-mi/articles/6727026.html
相关资源:log4j常用Appender配置