log4j自定義Appender

一、背景

雖然log4j很強大,能夠將日誌輸出到文件、DB、ES等。可是有時候確不免徹底適合本身,此時咱們就須要自定義Appender,使日誌輸出到指定的位置上。java

本文,將經過兩個例子說明自定義APPender,一個是將日誌寫入文件中,另外一個是將日誌發送到遠程Thrift服務中。git

本文代碼詳見:https://github.com/hawkingfoo/log-demogithub

二、自定義文件Appender

2.1 定義文件Appender

先上代碼:spring

@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);
        }
    }
}

上面代碼有幾個須要注意的地方:mybatis

  • @Plugin..註解:這個註解,是爲了在以後配置log4j2.xml時,指定的Appender Tag。
  • 構造函數:除了使用父類的之外,也能夠增長一些本身的配置。
  • 重寫append()方法:這裏面須要實現具體的邏輯,日誌的去向。
  • createAppender()方法:主要是接收log4j2.xml中的配置項。

2.2 添加log4j2.xml配置

<?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>

備註:app

  • 上面的log配置,一共配了2個輸出。一個是終端輸出,一個是採用自定義的FileAppender輸出到文件中。
  • <FileAppender>標籤要與自定義Appender中的類註解保持一致。

2.3 測試

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");
    }
}

日誌輸出ide

日誌輸出函數

能夠看到,日誌一共輸出了2份,一份到終端中,一份到log.log中(具體的文件路徑可在log4j2.xml中配置)。測試

三、自定義Thrift Appender

上一節,主要是日誌的文件輸出。有時咱們須要將日誌發送給日誌收集服務,常見的方法能夠寫一個日誌收集Agent,收集日誌;或者將日誌輸出方當成客戶端直接發送到遠程。this

下文,經過自定義Appender的方式,將日誌輸出到遠程的RPC服務中。

3.1 Thrift RPC服務

假設如今有一個Thrift RPC服務,實時接收日誌消息。它的定義是下面的樣子:

namespace java thrift

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

服務很簡單,入參是log,返回值是String。
Thrift相關知識能夠查看,Thrift RPC服務10分鐘上手

3.2 定義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);
        }
    }
}

備註:
除了和文件Appender相同的外,這裏須要注意兩個地方。一個是Thrift Client的建立,另外一個Thrift發送log。
具體的發送邏輯,在append()方法中實現。

3.3 添加log4j2.xml配置

<?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>

這裏一樣是定義了兩個輸出路徑,一個是終端,一個是Thrift服務。

3.4 測試

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端

Client端

能夠看出,Server端成功接收到了log。

做者:唐影若凡 來源:簡書

相關文章
相關標籤/搜索