编写自定义appender 的 步骤
1. 扩展 AppenderSkeleton 抽象类。如果是通过流方式实现读写数据的话,自定一定appender可以从WriterAppender继承,这样只需要把我们自己的 OutputStream连接到WriterAppender.qw上就可以了。更方便快捷。
2. 指定您的 appender 是否需要 layout。这个由requiresLayout()方法确定。
3. 如果某些属性必须同时激活,则应该在 activateOptions() 方法内完成。该方法上在Appender的构造函数之后被调用的。
4. 实现 close() 方法。它必须把 closed 字段的值设置为 true 。记得释放所有资源。
5. 可选地指定要使用的默认 ErrorHandler 对象。系统默认为OnlyOnceErrorHandler,它发送出第一个错误的消息并忽略其余的所有错误,错误消息将输出到 System.err。
6. 编写 append() 方法的代码。这个方法负责附加日志记录事件,并在错误发生时负责调用错误处理程序。我们主要的日志记录等处理任务实际上是在该append()方法内完成的。
请看程序。
由测试程序:Log4jTest.java、
自定义的Appdender:UDPAppender
配置文件:log4j.properties
三个源文件组成。
测试程序:Log4jTest.java
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
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 <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 = host.trim();
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 声明的时候。配置器类调用 Class.newInstance(YourCustomAppender.class) ,这等价于动态调用 new YourCustomAppender() 。框架这样做是为了避免被硬编码为任何特定的 appender 名称;框架是通用的,适用于任何 appender。
e.g. log4j.appender.file=org.apache.log4j.RollingFileAppender
3. 框架判断 appender 是否需要 layout。如果该 appender 不需要 layout,配置器就不会尝试从配置脚本中加载 layout 信息。
在Appender接口中,有public boolean requiresLayout() 方法,如果return true;则要求layout,需在配置脚本中设置layout信息
e.g. log4j.appender.file.layout=org.apache.log4j.PatternLayout
4. Log4j 配置器调用 setter 方法。在所有属性都已设置好之后,框架就会调用这个方法。
此时对应RollingFileAppender的每个Field,需要有一个setter方法,在配置脚本中也要进行设置
e.g. log4j.appender.file.File=<project>.log
对应的在RollingFileAppender中,有
public void setFile(String file) {
String val = file.trim();
fileName = val;
}
其它的属性,如MaxFileSize ,也相同
5. 配置器调用 activateOptions() 方法。在所有属性都已设置好之后,框架就会调用这个方法。
OptionHandler 接口定义了activateOptions()方法,在全部属性设置好了之后,在该方法中进行必要的操作,如打开文件:
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?");
}
6. Appender 准备就绪。 此刻,框架可以调用 append() 方法来处理日志记录请求。这个方法由 AppenderSkeleton.doAppend() 方法调用。
该方法为Appender中最关键的方法,append()中可以定义日志的输出,最简单的:
protected void append(LoggingEvent event){
//if layout is required
System.out.println(this.getLayout().format(event));
}
7. 最后,关闭appender。 当框架即将要删除您的自定义 appender 实例时,它会调用您的 appender 的 close() 方法。 close() 是一个清理方法,意味着 您需要释放已分配的所有资源。它是一个必需的方法,并且不接受任何参数。它必须把 closed 字段设置为 true ,并在有人尝试使用关闭的 appender 时向框架发出警报。
public void close() {
if (this.closed)
return;
this.closed = true;
}
注意: 需要在主程序退出前,调用Logger.getRoot().removeAllAppender();这样才能调用Appender的close()方法.
编写自定义appender 的 步骤
1. 扩展 AppenderSkeleton 抽象类。
2. 指定您的 appender 是否需要 layout。
3. 如果某些属性必须同时激活,则应该在 activateOptions() 方法内完成。
4. 实现 close() 方法。它必须把 closed 字段的值设置为 true 。记得释放所有资源。
5. 可选地指定要使用的默认 ErrorHandler 对象。系统默认为OnlyOnceErrorHandler,它发送出第一个错误的消息并忽略其余的所有错误,错误消息将输出到 System.err。
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 event); ErrorHandler getErrorHandler(); Filter getFilter(); Layout getLayout(); String getName(); boolean requiresLayout(); void setErrorHandler(ErrorHandler errorHandler); void setLayout(Layout layout); void setName(String name); }
这些方法处理 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
。
- 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 时向框架发出警报。
相关推荐
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000 # 发送日志给邮件 log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender log4j.appender....
5. **注册Appender**:在Log4j的配置文件(如log4j.properties或log4j.xml)中,声明你的自定义Appender,并配置相关属性。例如: ```xml <appender name="MyCustomAppender" class=...
- `log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender`:指定了名为`CONSOLE`的appender是控制台输出。 - `log4j.appender.CONSOLE.Target=System.out`:日志输出到标准输出流(System.out)。 - `log4j....
自定义 Log4J 的 SocketAppender,在日志服务器和调用事件的各种客户端之间进行通信。 如何使用:AsynchListener - 通知程序 - 当日志服务器在特定端口和机器上准备就绪时,通知日志客户端是否准备好调用通知。 ...
配置文件(log4j.properties)中,你需要将这个自定义的Appender和Layout加入: ```properties log4j.appender.customAppender=your.package.CustomLogAppender log4j.appender.customAppender.layout=your.package...
在提供的压缩包中,我们看到了四个文件:log4j-core-2.3.jar、log4j-1.2.17.jar、log4j-api-2.3.jar以及log4j.properties。这里,log4j-core和log4j-api是Log4j 2.x版本的核心库,它们提供了日志记录的功能实现。log...
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n log4j.appender.file=org.apache.log4j....
总结,SLF4J和Log4j的组合使用让日志管理更加灵活,开发者可以通过SLF4J的简洁API进行日志记录,同时利用Log4j的强大功能,如自定义输出格式和多种输出目的地。通过适当的配置和测试,我们可以确保日志系统按照预期...
《深入理解Log4j自定义》 在Java开发中,日志系统扮演着至关重要的角色,它能够帮助我们记录程序运行过程中的各种信息,便于调试、监控和问题排查。Log4j作为一款广泛使用的日志框架,其强大的可配置性和自定义能力...
log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n # 文件Appender log4j.appender.File=org.apache...
log4j.appender.ERROR.layout=org.apache.log4j.PatternLayout log4j.appender.ERROR.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n # 定义一个名为INFO的Appender,用于记录信息级别及以上的日志 ...
6. **自定义日志级别**:除了内置的日志级别(如DEBUG、INFO、WARN、ERROR、FATAL),Log4j2还支持用户自定义的日志级别,可以通过代码或配置文件进行定义。 7. **消息对象支持**:用户可以创建自定义的消息类型,...
要实现输出多个自定义路径的日志文件,我们需要在Log4j的配置文件(通常是`log4j.properties`或`log4j.xml`)中定义多个Appender。Appender是Log4j中负责将日志信息输出到指定目的地的组件,例如文件、控制台、网络...
2. **编写初始化 Servlet**:创建一个自定义的 Servlet 来初始化 Log4j。该 Servlet 的主要任务是在启动时加载特定的 `log4j.properties` 文件。 ```java public class Log4jInitServlet extends HttpServlet { ...
Log4j还提供了许多高级特性,如过滤器(Filter)来控制哪些日志信息会被记录,自定义布局(Custom Layout)来实现特定的日志格式,以及异步日志记录以提高性能。此外,还可以通过引入其他模块如SMTPAppender发送邮件...
本文将详细讲解如何实现自定义异常以及如何有效地利用Log4j进行日志管理。 首先,让我们来了解自定义异常。在Java中,当我们遇到特定的业务逻辑错误或需要更精确地捕获和处理错误时,我们可以创建自定义异常。...
Log4j允许开发者自定义日志输出的方式和格式,通过继承Appender和Layout接口,可以实现如电子邮件通知、日志发送到服务器或其他特殊格式的需求。 六、性能优化 在大型系统中,日志处理可能成为性能瓶颈。Log4j提供...
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender log4j.appender.R.File=./logs/app.log log4j.appender.R.ImmediateFlush=true log4j.appender.R.Append=true log4j.appender.R.Threshold=DEBUG ...
- Log4j允许开发者创建自定义的Appender和Layout来满足特定需求,如发送邮件、写入数据库等。 - Appender决定了日志信息的输出位置,而Layout则决定日志的显示格式。 6. **Log4j的最新版本:Log4j 2** - 随着...
四、Log4j的高级特性 1. **异步日志记录**:Log4j支持异步日志记录,通过AsyncAppender可以在不阻塞主线程的情况下高效地处理日志。 2. **自定义Appender和Layout**:如果内置的Appender和Layout无法满足需求,...