雖然log4j很強大,能夠將日誌輸出到文件、DB、ES等。可是有時候確不免徹底適合本身,此時咱們就須要自定義Appender,使日誌輸出到指定的位置上。java
本文,將經過兩個例子說明自定義APPender,一個是將日誌寫入文件中,另外一個是將日誌發送到遠程Thrift服務中。git
本文代碼詳見:https://github.com/hawkingfoo/log-demogithub
先上代碼: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中的配置項。<?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
<FileAppender>
標籤要與自定義Appender中的類註解保持一致。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中配置)。測試
上一節,主要是日誌的文件輸出。有時咱們須要將日誌發送給日誌收集服務,常見的方法能夠寫一個日誌收集Agent,收集日誌;或者將日誌輸出方當成客戶端直接發送到遠程。this
下文,經過自定義Appender的方式,將日誌輸出到遠程的RPC服務中。
假設如今有一個Thrift RPC服務,實時接收日誌消息。它的定義是下面的樣子:
namespace java thrift service LogServer { string getLogRes(1:string log); }
服務很簡單,入參是log,返回值是String。
Thrift相關知識能夠查看,Thrift RPC服務10分鐘上手。
@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()
方法中實現。
<?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服務。
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。
做者:唐影若凡 來源:簡書