log4j custom Appender

1. Background

While log4j is powerful, it can output logs to files, DB, ES, etc. But sometimes it is inevitable that it is completely suitable for us. At this time, we need to customize the Appender to output the log to the specified location.

In this article, two examples will be used to illustrate custom APPender, one is to write logs to a file, and the other is to send logs to a remote Thrift service.

See the code of this article for details: https://github.com/hawkingfoo/log-demo

2. Custom file Appender

2.1 Definition file Appender

Code first:

@Plugin(name = "FileAppender", category = "Core", elementType = "appender", printObject = true)
public class FileAppender extends AbstractAppender {
    private String fileName;

    /* 构造函数 */
    public FileAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String fileName) {
        super(name, filter, layout, ignoreExceptions);
        this.fileName = fileName;
    }

    @Override
    public void append(LogEvent event) {
        final byte[] bytes = getLayout().toByteArray(event);
        writerFile(bytes);

    }

    /*  接收配置文件中的参数 */
    @PluginFactory
    public static FileAppender createAppender(@PluginAttribute("name") String name,
                                              @PluginAttribute("fileName") String fileName,
                                              @PluginElement("Filter") final Filter filter,
                                              @PluginElement("Layout") Layout<? extends Serializable> layout,
                                              @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
        if (name == null) {
            LOGGER.error("no name defined in conf.");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        // 创建文件
        if (!createFile(fileName)) {
            return null;
        }
        return new FileAppender(name, filter, layout, ignoreExceptions, fileName);
    }

    private static boolean createFile(String fileName) {
        Path filePath = Paths.get(fileName);
        try {
            // 每次都重新写文件,不追加
            if (Files.exists(filePath)) {
                Files.delete(filePath);
            }
            Files.createFile(filePath);
        } catch (IOException e) {
            LOGGER.error("create file exception", e);
            return false;
        }
        return true;
    }

    private void writerFile(byte[] log) {
        try {
            Files.write(Paths.get(fileName), log, StandardOpenOption.APPEND);
        } catch (IOException e) {
            LOGGER.error("write file exception", e);
        }
    }
}

There are a few things to note in the above code:

  • @Plugin..Annotation: This annotation is for the log4j2.xmlspecified Appender Tag when configuring later.
  • Constructor: In addition to using the parent class, you can also add some of your own configuration.
  • Overriding append()method: It is necessary to implement specific logic and log whereabouts.
  • createAppender()Method: Mainly to receive the configuration items in log4j2.xml.

2.2 Add log4j2.xml configuration

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

<configuration status="INFO" monitorInterval="30">
    <appenders>
        <!--这个输出控制台的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
        </console>

        <!-- 这个就是自定义的Appender -->
        <FileAppender name="File" fileName="log.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
        </FileAppender>

    </appenders>

    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="File"/>
        </root>
    </loggers>
</configuration>

Remark:

  • The above log configuration has a total of 2 outputs. One is terminal output, and the other is output to a file using a custom FileAppender.
  • <FileAppender>The label should be consistent with the class annotation in the custom Appender.

2.3 Testing

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

    public static void main(String[] args) {
        logger.info("1");
        logger.info("2");
        logger.info("3");
    }
}

log output

log output

It can be seen that a total of 2 logs are output, one to the terminal and one to the log.logmiddle (the specific file path can be configured in log4j2.xml).

3. Custom Thrift Appender

In the previous section, it is mainly the file output of the log. Sometimes we need to send logs to the log collection service. Common methods can be to write a log collection agent to collect logs; or use the log output party as a client to send it directly to the remote.

Below, the log is output to the remote RPC service by customizing the Appender.

3.1 Thrift RPC service

Suppose there is now a Thrift RPC service that receives log messages in real time. Its definition is as follows:

namespace java thrift

service LogServer {
    string getLogRes(1:string log);
}

The service is very simple, the input parameter is log, and the return value is String.
Thrift related knowledge can be viewed, and the Thrift RPC service can be started in 10 minutes .

3.2 Define ThriftAppender

@Plugin(name = "ThriftAppender", category = "Core", elementType = "appender", printObject = true)
public class ThriftAppender extends AbstractAppender {

    private LogServer.Client client;
    private TTransport transport;

    /* 构造函数 */
    public ThriftAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String host) {
        super(name, filter, layout, ignoreExceptions);
        // 创建客户端
        createThriftClient(host);
    }

    @Override
    public void append(LogEvent event) {
        final byte[] bytes = getLayout().toByteArray(event);
        try {
            String response = client.getLogRes(new String(bytes));
            System.out.println(response);
        } catch (TException e) {
            e.printStackTrace();
        }
    }

    /*  接收配置文件中的参数 */
    @PluginFactory
    public static ThriftAppender createAppender(@PluginAttribute("name") String name,
                                              @PluginAttribute("host") String host,
                                              @PluginElement("Filter") final Filter filter,
                                              @PluginElement("Layout") Layout<? extends Serializable> layout,
                                              @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
        if (name == null) {
            LOGGER.error("no name defined in conf.");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        return new ThriftAppender(name, filter, layout, ignoreExceptions, host);
    }

    @Override
    public void stop() {
        if (transport != null) {
            transport.close();
        }
    }

    private void createThriftClient(String host) {
        try {
            transport = new TFramedTransport(new TSocket(host, 9000));
            transport.open();
            TProtocol protocol = new TBinaryProtocol(transport);
            client = new LogServer.Client(protocol);
            LOGGER.info("create client success");
        } catch (Exception e) {
            LOGGER.error("create file exception", e);
        }
    }
}

Note:
In addition to the same as the file Appender, there are two places to pay attention to here. One is the creation of Thrift Client, and the other Thrift sends log.
The specific sending logic append()is implemented in the method.

3.3 Add log4j2.xml configuration

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

<configuration status="INFO" monitorInterval="30">
    <appenders>
        <!--这个输出控制台的配置-->
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
        </console>

        <!-- 这个就是自定义的Appender -->
        <ThriftAppender name="Thrift" host="127.0.0.1">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
        </ThriftAppender>
    </appenders>

    <loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="Thrift"/>

        </root>
    </loggers>
</configuration>

Two output paths are also defined here, one is the terminal and the other is the Thrift service.

3.4 Testing

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

    public static void main(String[] args) {
        logger.info("a");
        logger.info("b");
        logger.info("c");
    }
}

Server side

Client

It can be seen that the server side successfully received the log.


Author: Tang Yingruofan Source: Jianshu

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324418582&siteId=291194637