zoukankan      html  css  js  c++  java
  • 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.  
      /* 构造函数 */
    6.  
      public FileAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String fileName) {
    7.  
      super(name, filter, layout, ignoreExceptions);
    8.  
      this.fileName = fileName;
    9.  
      }
    10.  
       
    11.  
      @Override
    12.  
      public void append(LogEvent event) {
    13.  
      final byte[] bytes = getLayout().toByteArray(event);
    14.  
      writerFile(bytes);
    15.  
       
    16.  
      }
    17.  
       
    18.  
      /* 接收配置文件中的参数 */
    19.  
      @PluginFactory
    20.  
      public static FileAppender createAppender(@PluginAttribute("name") String name,
    21.  
      @PluginAttribute("fileName") String fileName,
    22.  
      @PluginElement("Filter") final Filter filter,
    23.  
      @PluginElement("Layout") Layout<? extends Serializable> layout,
    24.  
      @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
    25.  
      if (name == null) {
    26.  
      LOGGER.error("no name defined in conf.");
    27.  
      return null;
    28.  
      }
    29.  
      if (layout == null) {
    30.  
      layout = PatternLayout.createDefaultLayout();
    31.  
      }
    32.  
      // 创建文件
    33.  
      if (!createFile(fileName)) {
    34.  
      return null;
    35.  
      }
    36.  
      return new FileAppender(name, filter, layout, ignoreExceptions, fileName);
    37.  
      }
    38.  
       
    39.  
      private static boolean createFile(String fileName) {
    40.  
      Path filePath = Paths.get(fileName);
    41.  
      try {
    42.  
      // 每次都重新写文件,不追加
    43.  
      if (Files.exists(filePath)) {
    44.  
      Files.delete(filePath);
    45.  
      }
    46.  
      Files.createFile(filePath);
    47.  
      } catch (IOException e) {
    48.  
      LOGGER.error("create file exception", e);
    49.  
      return false;
    50.  
      }
    51.  
      return true;
    52.  
      }
    53.  
       
    54.  
      private void writerFile(byte[] log) {
    55.  
      try {
    56.  
      Files.write(Paths.get(fileName), log, StandardOpenOption.APPEND);
    57.  
      } catch (IOException e) {
    58.  
      LOGGER.error("write file exception", e);
    59.  
      }
    60.  
      }
    61.  
      }

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

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

    2.2 添加log4j2.xml配置

    1.  
      <?xml version="1.0" encoding="UTF-8"?>
    2.  
       
    3.  
      <configuration status="INFO" monitorInterval="30">
    4.  
      <appenders>
    5.  
      <!--这个输出控制台的配置-->
    6.  
      <console name="Console" target="SYSTEM_OUT">
    7.  
      <!--输出日志的格式-->
    8.  
      <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
    9.  
      </console>
    10.  
       
    11.  
      <!-- 这个就是自定义的Appender -->
    12.  
      <FileAppender name="File" fileName="log.log">
    13.  
      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
    14.  
      </FileAppender>
    15.  
       
    16.  
      </appenders>
    17.  
       
    18.  
      <loggers>
    19.  
      <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
    20.  
      <logger name="org.springframework" level="INFO"></logger>
    21.  
      <logger name="org.mybatis" level="INFO"></logger>
    22.  
      <root level="all">
    23.  
      <appender-ref ref="Console"/>
    24.  
      <appender-ref ref="File"/>
    25.  
      </root>
    26.  
      </loggers>
    27.  
      </configuration>

    备注:

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

    2.3 测试

    1.  
      public class TestLogFile {
    2.  
      private static final Logger logger = LogManager.getLogger(TestLogFile.class);
    3.  
       
    4.  
      public static void main(String[] args) {
    5.  
      logger.info("1");
    6.  
      logger.info("2");
    7.  
      logger.info("3");
    8.  
      }
    9.  
      }

    日志输出

    日志输出

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

    3、自定义Thrift Appender

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

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

    3.1 Thrift RPC服务

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

    1.  
      namespace java thrift
    2.  
       
    3.  
      service LogServer {
    4.  
      string getLogRes(1:string log);
    5.  
      }

    服务很简单,入参是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.  
       
    4.  
      private LogServer.Client client;
    5.  
      private TTransport transport;
    6.  
       
    7.  
      /* 构造函数 */
    8.  
      public ThriftAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String host) {
    9.  
      super(name, filter, layout, ignoreExceptions);
    10.  
      // 创建客户端
    11.  
      createThriftClient(host);
    12.  
      }
    13.  
       
    14.  
      @Override
    15.  
      public void append(LogEvent event) {
    16.  
      final byte[] bytes = getLayout().toByteArray(event);
    17.  
      try {
    18.  
      String response = client.getLogRes(new String(bytes));
    19.  
      System.out.println(response);
    20.  
      } catch (TException e) {
    21.  
      e.printStackTrace();
    22.  
      }
    23.  
      }
    24.  
       
    25.  
      /* 接收配置文件中的参数 */
    26.  
      @PluginFactory
    27.  
      public static ThriftAppender createAppender(@PluginAttribute("name") String name,
    28.  
      @PluginAttribute("host") String host,
    29.  
      @PluginElement("Filter") final Filter filter,
    30.  
      @PluginElement("Layout") Layout<? extends Serializable> layout,
    31.  
      @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
    32.  
      if (name == null) {
    33.  
      LOGGER.error("no name defined in conf.");
    34.  
      return null;
    35.  
      }
    36.  
      if (layout == null) {
    37.  
      layout = PatternLayout.createDefaultLayout();
    38.  
      }
    39.  
      return new ThriftAppender(name, filter, layout, ignoreExceptions, host);
    40.  
      }
    41.  
       
    42.  
      @Override
    43.  
      public void stop() {
    44.  
      if (transport != null) {
    45.  
      transport.close();
    46.  
      }
    47.  
      }
    48.  
       
    49.  
      private void createThriftClient(String host) {
    50.  
      try {
    51.  
      transport = new TFramedTransport(new TSocket(host, 9000));
    52.  
      transport.open();
    53.  
      TProtocol protocol = new TBinaryProtocol(transport);
    54.  
      client = new LogServer.Client(protocol);
    55.  
      LOGGER.info("create client success");
    56.  
      } catch (Exception e) {
    57.  
      LOGGER.error("create file exception", e);
    58.  
      }
    59.  
      }
    60.  
      }

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

    3.3 添加log4j2.xml配置

    1.  
      <?xml version="1.0" encoding="UTF-8"?>
    2.  
       
    3.  
      <configuration status="INFO" monitorInterval="30">
    4.  
      <appenders>
    5.  
      <!--这个输出控制台的配置-->
    6.  
      <console name="Console" target="SYSTEM_OUT">
    7.  
      <!--输出日志的格式-->
    8.  
      <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
    9.  
      </console>
    10.  
       
    11.  
      <!-- 这个就是自定义的Appender -->
    12.  
      <ThriftAppender name="Thrift" host="127.0.0.1">
    13.  
      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
    14.  
      </ThriftAppender>
    15.  
      </appenders>
    16.  
       
    17.  
      <loggers>
    18.  
      <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
    19.  
      <logger name="org.springframework" level="INFO"></logger>
    20.  
      <logger name="org.mybatis" level="INFO"></logger>
    21.  
      <root level="all">
    22.  
      <appender-ref ref="Console"/>
    23.  
      <appender-ref ref="Thrift"/>
    24.  
       
    25.  
      </root>
    26.  
      </loggers>
    27.  
      </configuration>

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

    3.4 测试

    1.  
      public class TestThriftFile {
    2.  
      private static final Logger logger = LogManager.getLogger(TestThriftFile.class);
    3.  
       
    4.  
      public static void main(String[] args) {
    5.  
      logger.info("a");
    6.  
      logger.info("b");
    7.  
      logger.info("c");
    8.  
      }
    9.  
      }

    Server端

    Client端

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

  • 相关阅读:
    mac下更改MySQL的默认编码
    pycharm使用gitlab输错密码解决办法
    Django中form组件的is_valid校验机制
    装饰器整理
    粘包
    MySQL常见数据库引擎及对比
    jtag、在线仿真器
    《如何高效学习》-整体性学习策略
    keil5到iar8的使用配置迁移
    Python3学习(1)——初步了解
  • 原文地址:https://www.cnblogs.com/goody9807/p/9780982.html
Copyright © 2011-2022 走看看