log4j2实现自定义Appender(输出到文件/RPC服务中)

1、背景

虽然log4j很强大,可以将日志输出到文件、DB、ES等。但是有时候确难免完全适合自己,此时我们就需要自定义Appender,使日志输出到指定的位置上。

本文,将通过两个例子说明自定义APPender,一个是将日志写入文件中,另一个是将日志发送到远程Thrift服务中。

本文代码详见:https://github.com/hawkingfoo/log-demo

2、自定义文件Appender

2.1 定义文件Appender

先上代码:

 
  1. @Plugin(name = "FileAppender", category = "Core", elementType = "appender", printObject = true)

  2. public class FileAppender extends AbstractAppender {

  3. private String fileName;

  4. /* 构造函数 */

  5. public FileAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String fileName) {

  6. super(name, filter, layout, ignoreExceptions);

  7. this.fileName = fileName;

  8. }

  9. @Override

  10. public void append(LogEvent event) {

  11. final byte[] bytes = getLayout().toByteArray(event);

  12. writerFile(bytes);

  13. }

  14. /* 接收配置文件中的参数 */

  15. @PluginFactory

  16. public static FileAppender createAppender(@PluginAttribute("name") String name,

  17. @PluginAttribute("fileName") String fileName,

  18. @PluginElement("Filter") final Filter filter,

  19. @PluginElement("Layout") Layout<? extends Serializable> layout,

  20. @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {

  21. if (name == null) {

  22. LOGGER.error("no name defined in conf.");

  23. return null;

  24. }

  25. if (layout == null) {

  26. layout = PatternLayout.createDefaultLayout();

  27. }

  28. // 创建文件

  29. if (!createFile(fileName)) {

  30. return null;

  31. }

  32. return new FileAppender(name, filter, layout, ignoreExceptions, fileName);

  33. }

  34. private static boolean createFile(String fileName) {

  35. Path filePath = Paths.get(fileName);

  36. try {

  37. // 每次都重新写文件,不追加

  38. if (Files.exists(filePath)) {

  39. Files.delete(filePath);

  40. }

  41. Files.createFile(filePath);

  42. } catch (IOException e) {

  43. LOGGER.error("create file exception", e);

  44. return false;

  45. }

  46. return true;

  47. }

  48. private void writerFile(byte[] log) {

  49. try {

  50. Files.write(Paths.get(fileName), log, StandardOpenOption.APPEND);

  51. } catch (IOException e) {

  52. LOGGER.error("write file exception", e);

  53. }

  54. }

  55. }

上面代码有几个需要注意的地方:

  • @Plugin..注解:这个注解,是为了在之后配置log4j2.xml时,指定的Appender Tag。
  • 构造函数:除了使用父类的以外,也可以增加一些自己的配置。
  • 重写append()方法:这里面需要实现具体的逻辑,日志的去向。
  • createAppender()方法:主要是接收log4j2.xml中的配置项。

2.2 添加log4j2.xml配置

 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <configuration status="INFO" monitorInterval="30">

  3. <appenders>

  4. <!--这个输出控制台的配置-->

  5. <console name="Console" target="SYSTEM_OUT">

  6. <!--输出日志的格式-->

  7. <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>

  8. </console>

  9. <!-- 这个就是自定义的Appender -->

  10. <FileAppender name="File" fileName="log.log">

  11. <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />

  12. </FileAppender>

  13. </appenders>

  14. <loggers>

  15. <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->

  16. <logger name="org.springframework" level="INFO"></logger>

  17. <logger name="org.mybatis" level="INFO"></logger>

  18. <root level="all">

  19. <appender-ref ref="Console"/>

  20. <appender-ref ref="File"/>

  21. </root>

  22. </loggers>

  23. </configuration>

备注:

  • 上面的log配置,一共配了2个输出。一个是终端输出,一个是采用自定义的FileAppender输出到文件中。
  • <FileAppender>标签要与自定义Appender中的类注解保持一致。

2.3 测试

 
  1. public class TestLogFile {

  2. private static final Logger logger = LogManager.getLogger(TestLogFile.class);

  3. public static void main(String[] args) {

  4. logger.info("1");

  5. logger.info("2");

  6. logger.info("3");

  7. }

  8. }

可以看到,日志一共输出了2份,一份到终端中,一份到log.log中(具体的文件路径可在log4j2.xml中配置)。

 注意:

       编码完执行测试,在log4j2.xml中添加自定义的appender后运行可能会发现出现如下误: 

                  main ERROR Error processing element XXX ([Appenders: null]): CLASS_NOT_FOUND 

2016-08-02 22:46:30,693 main ERROR Error processing element XXX ([Appenders: null]): CLASS_NOT_FOUND

       这是由于log4j2没有加载自定义的类而抛出异常。可以在log4j2.xml中加上 :

                    <Configuration status="info" packages="com.custom.log.log4j2plugin">  

<Configuration status="info" packages="com.zero.scribe.log4j2plugin">

       这里的packages是自定义的Appender类所在包的包名,这会告诉log4j2去加载这个包下的类,而不会抛出CLASS_NOT_FOUND异常。
       倘若不想多加packages这个字段,则需要预先build一下project,我使用的是gradle构建项目,运行gradle build后在classes中会额外生成META-INF,META-INF中会有org.apache.logging.log4j.core.config.plugins目录,目录会有Log4j2Plugins.dat,该二进制文件则会告诉log4j2需要加载自定义的Appender类。

3、自定义Thrift Appender

上一节,主要是日志的文件输出。有时我们需要将日志发送给日志收集服务,常见的方法可以写一个日志收集Agent,收集日志;或者将日志输出方当成客户端直接发送到远程。

下文,通过自定义Appender的方式,将日志输出到远程的RPC服务中。

3.1 Thrift RPC服务

假设现在有一个Thrift RPC服务,实时接收日志消息。它的定义是下面的样子:

 
  1. namespace java thrift

  2. service LogServer {

  3. string getLogRes(1:string log);

  4. }

服务很简单,入参是log,返回值是String。
Thrift相关知识可以查看,Thrift RPC服务10分钟上手

3.2 定义ThriftAppender

 
  1. @Plugin(name = "ThriftAppender", category = "Core", elementType = "appender", printObject = true)

  2. public class ThriftAppender extends AbstractAppender {

  3. private LogServer.Client client;

  4. private TTransport transport;

  5. /* 构造函数 */

  6. public ThriftAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String host) {

  7. super(name, filter, layout, ignoreExceptions);

  8. // 创建客户端

  9. createThriftClient(host);

  10. }

  11. @Override

  12. public void append(LogEvent event) {

  13. final byte[] bytes = getLayout().toByteArray(event);

  14. try {

  15. String response = client.getLogRes(new String(bytes));

  16. System.out.println(response);

  17. } catch (TException e) {

  18. e.printStackTrace();

  19. }

  20. }

  21. /* 接收配置文件中的参数 */

  22. @PluginFactory

  23. public static ThriftAppender createAppender(@PluginAttribute("name") String name,

  24. @PluginAttribute("host") String host,

  25. @PluginElement("Filter") final Filter filter,

  26. @PluginElement("Layout") Layout<? extends Serializable> layout,

  27. @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {

  28. if (name == null) {

  29. LOGGER.error("no name defined in conf.");

  30. return null;

  31. }

  32. if (layout == null) {

  33. layout = PatternLayout.createDefaultLayout();

  34. }

  35. return new ThriftAppender(name, filter, layout, ignoreExceptions, host);

  36. }

  37. @Override

  38. public void stop() {

  39. if (transport != null) {

  40. transport.close();

  41. }

  42. }

  43. private void createThriftClient(String host) {

  44. try {

  45. transport = new TFramedTransport(new TSocket(host, 9000));

  46. transport.open();

  47. TProtocol protocol = new TBinaryProtocol(transport);

  48. client = new LogServer.Client(protocol);

  49. LOGGER.info("create client success");

  50. } catch (Exception e) {

  51. LOGGER.error("create file exception", e);

  52. }

  53. }

  54. }

备注:
除了和文件Appender相同的外,这里需要注意两个地方。一个是Thrift Client的创建,另一个Thrift发送log。
具体的发送逻辑,在append()方法中实现。

3.3 添加log4j2.xml配置

 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <configuration status="INFO" monitorInterval="30">

  3. <appenders>

  4. <!--这个输出控制台的配置-->

  5. <console name="Console" target="SYSTEM_OUT">

  6. <!--输出日志的格式-->

  7. <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>

  8. </console>

  9. <!-- 这个就是自定义的Appender -->

  10. <ThriftAppender name="Thrift" host="127.0.0.1">

  11. <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />

  12. </ThriftAppender>

  13. </appenders>

  14. <loggers>

  15. <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->

  16. <logger name="org.springframework" level="INFO"></logger>

  17. <logger name="org.mybatis" level="INFO"></logger>

  18. <root level="all">

  19. <appender-ref ref="Console"/>

  20. <appender-ref ref="Thrift"/>

  21. </root>

  22. </loggers>

  23. </configuration>

这里同样是定义了两个输出路径,一个是终端,一个是Thrift服务。

3.4 测试

 
  1. public class TestThriftFile {

  2. private static final Logger logger = LogManager.getLogger(TestThriftFile.class);

  3. public static void main(String[] args) {

  4. logger.info("a");

  5. logger.info("b");

  6. logger.info("c");

  7. }

  8. }

测试可以看出,Server端成功接收到了log。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 `log4j.properties` 配置自定义日志输出路径,可以按照以下步骤进行操作: 1. 在 `log4j.properties` 文件添加一个新的 appender,指定日志输出路径。例如: ```properties log4j.appender.custom=org.apache.log4j.RollingFileAppender log4j.appender.custom.File=/path/to/custom.log log4j.appender.custom.MaxFileSize=10MB log4j.appender.custom.MaxBackupIndex=10 log4j.appender.custom.layout=org.apache.log4j.PatternLayout log4j.appender.custom.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c %x - %m%n ``` 在上面的配置,`custom` 是 appender 的名称,`/path/to/custom.log` 是自定义日志输出路径,`MaxFileSize` 和 `MaxBackupIndex` 分别指定文件大小和备份文件数。 2. 在 `log4j.properties` 文件指定日志输出目的地。例如: ```properties log4j.rootLogger=INFO, custom ``` 在上面的配置,`custom` 是上一步定义的 appender 名称。 3. 在代码获取 Logger 对象并使用。例如: ```java import org.apache.log4j.Logger; public class MyApplication { private static final Logger logger = Logger.getLogger(MyApplication.class); public static void main(String[] args) { logger.info("Hello, world!"); } } ``` 在上面的代码,使用 `Logger.getLogger(MyApplication.class)` 获取 Logger 对象,并调用 `logger.info("Hello, world!")` 输出日志信息。日志信息将会输出自定义的日志输出路径 `/path/to/custom.log`。 注意:如果你已经在 `log4j.properties` 文件定义了其他的 appender 和日志输出目的地,需要将自定义appender 和日志输出目的地添加到现有配置。例如: ```properties log4j.rootLogger=INFO, console, file, custom log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Target=System.out log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c %x - %m%n log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=/path/to/file.log log4j.appender.file.MaxFileSize=10MB log4j.appender.file.MaxBackupIndex=10 log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c %x - %m%n log4j.appender.custom=org.apache.log4j.RollingFileAppender log4j.appender.custom.File=/path/to/custom.log log4j.appender.custom.MaxFileSize=10MB log4j.appender.custom.MaxBackupIndex=10 log4j.appender.custom.layout=org.apache.log4j.PatternLayout log4j.appender.custom.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c %x - %m%n ``` 在上面的配置,`console` 和 `file` 是已经定义的 appender 和日志输出目的地,`custom` 是自定义appender 和日志输出目的地。注意在 `log4j.rootLogger` 将它们都添加进去。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值