log4j2自定義Appender(輸出到檔案/RPC服務中)
1、背景
雖然log4j很強大,可以將日誌輸出到檔案、DB、ES等。但是有時候確難免完全適合自己,此時我們就需要自定義Appender,使日誌輸出到指定的位置上。
本文,將通過兩個例子說明自定義APPender,一個是將日誌寫入檔案中,另一個是將日誌傳送到遠端Thrift服務中。
本文程式碼詳見:ofollow,noindex" target="_blank">https://github.com/hawkingfoo/log-demo
2、自定義檔案Appender
2.1 定義檔案Appender
先上程式碼:
-
@Plugin(name = "FileAppender", category = "Core", elementType ="appender", printObject = true)
-
public class FileAppender extendsAbstractAppender {
-
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.");
-
returnnull;
-
}
-
if (layout ==null) {
-
layout = PatternLayout.createDefaultLayout();
-
}
-
// 建立檔案
-
if (!createFile(fileName)) {
-
returnnull;
-
}
-
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);
-
returnfalse;
-
}
-
returntrue;
-
}
-
private void writerFile(byte[] log) {
-
try {
-
Files.write(Paths.get(fileName), log, StandardOpenOption.APPEND);
-
}catch (IOException e) {
-
LOGGER.error("write file exception", e);
-
}
-
}
-
}
上面程式碼有幾個需要注意的地方:
-
@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>
備註:
<FileAppender>
2.3 測試
-
public classTestLogFile {
-
private staticfinal Logger logger = LogManager.getLogger(TestLogFile.class);
-
public static void main(String[] args) {
-
logger.info("1");
-
logger.info("2");
-
logger.info("3");
-
}
-
}
日誌輸出
日誌輸出
可以看到,日誌一共輸出了2份,一份到終端中,一份到log.log
中(具體的檔案路徑可在log4j2.xml中配置)。
3、自定義Thrift Appender
上一節,主要是日誌的檔案輸出。有時我們需要將日誌傳送給日誌收集服務,常見的方法可以寫一個日誌收集Agent,收集日誌;或者將日誌輸出方當成客戶端直接傳送到遠端。
下文,通過自定義Appender的方式,將日誌輸出到遠端的RPC服務中。
3.1 Thrift RPC服務
假設現在有一個Thrift RPC服務,實時接收日誌訊息。它的定義是下面的樣子:
-
namespace javathrift
-
serviceLogServer {
-
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 extendsAbstractAppender {
-
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) {
-
finalbyte[] 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.");
-
returnnull;
-
}
-
if (layout ==null) {
-
layout = PatternLayout.createDefaultLayout();
-
}
-
returnnew 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 classTestThriftFile {
-
private staticfinal 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。