log4j2實現自定義Appender(輸出到檔案/RPC服務中)
1、背景
雖然log4j很強大,可以將日誌輸出到檔案、DB、ES等。但是有時候確難免完全適合自己,此時我們就需要自定義Appender,使日誌輸出到指定的位置上。
本文,將通過兩個例子說明自定義APPender,一個是將日誌寫入檔案中,另一個是將日誌傳送到遠端Thrift服務中。
2、自定義檔案Appender
2.1 定義檔案Appender
先上程式碼:
@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);
}
}
}
上面程式碼有幾個需要注意的地方:
@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>
備註:
- 上面的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");
}
}
可以看到,日誌一共輸出了2份,一份到終端中,一份到log.log
中(具體的檔案路徑可在log4j2.xml中配置)。
注意:
編碼完執行測試,在log4j2.xml中新增自定義的appender後執行可能會發現出現如下誤:
main ERROR Error processing element XXX ([Appenders: null]): CLASS_NOT_FOUND
2016-08-02 22:46:30,693 main ERROR Error processing element XXX ([Appenders: null]): CLASS_NOT_FOUND
這是由於log4j2沒有載入自定義的類而丟擲異常。可以在log4j2.xml中加上 :
<Configuration status="info" packages="com.custom.log.log4j2plugin">
<Configuration status="info" packages="com.zero.scribe.log4j2plugin">
這裡的packages是自定義的Appender類所在包的包名,這會告訴log4j2去載入這個包下的類,而不會丟擲CLASS_NOT_FOUND異常。
倘若不想多加packages這個欄位,則需要預先build一下project,我使用的是gradle構建專案,執行gradle build後在classes中會額外生成META-INF,META-INF中會有org.apache.logging.log4j.core.config.plugins目錄,目錄會有Log4j2Plugins.dat,該二進位制檔案則會告訴log4j2需要載入自定義的Appender類。
3、自定義Thrift Appender
上一節,主要是日誌的檔案輸出。有時我們需要將日誌傳送給日誌收集服務,常見的方法可以寫一個日誌收集Agent,收集日誌;或者將日誌輸出方當成客戶端直接傳送到遠端。
下文,通過自定義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端成功接收到了log。
相關推薦
log4j2實現自定義Appender(輸出到檔案/RPC服務中)
1、背景 雖然log4j很強大,可以將日誌輸出到檔案、DB、ES等。但是有時候確難免完全適合自己,此時我們就需要自定義Appender,使日誌輸出到指定的位置上。 本文,將通過兩個例子說明自定義APPender,一個是將日誌寫入檔案中,另一個是將日誌傳送到遠端Thrift服
log4j2自定義Appender(輸出到檔案/RPC服務中)
1、背景 雖然log4j很強大,可以將日誌輸出到檔案、DB、ES等。但是有時候確難免完全適合自己,此時我們就需要自定義Appender,使日誌輸出到指定的位置上。 本文,將通過兩個例子說明自定義APPender,一個是將日誌寫入檔案中,另一個是將日誌傳送到遠
thinkphp 5.0如何實現自定義404(異常處理)頁面
錯誤頁 自定義異常 異常錯誤 錯誤 load php 錯誤信息 art 正常 404頁面是客戶端在瀏覽網頁時,由於服務器無法正常提供信息,或是服務器無法回應,且不知道原因所返回的頁面。404承載著用戶體驗與SEO優化的重任。404頁面通常為用戶訪問了網站上不存在或已刪除的
java實現自定義佇列(先進先出)
題目:java實現自定義佇列(先進先出) 主類: import java.util.LinkedList; public class Main { public static void main(String[] args) { DuiLie dl
實現自定義註解(程式碼篇)
第一:定義三個自定義的註解類 第二:建立一個實體 第三:建立一箇中間類 第四:測試 註解處理的基礎知識 方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass
自定義RatingBar控制元件,實現可自定義星星(或專案所需圖片)的寬高,告別使用warp_content導致控制元件大小不可控
之前做過的一款app中涉及到RatingBar控制元件,基本上來說都會要求用圖片來替代, 即使同樣是星星(可能是覺得系統自帶的比較醜吧,不過我覺得還好啊)。 當時就覺得很難去做適配,UI給的圖片沒法去控制寬高,只能使用warp_content來做, 可是這樣會導
Ultra-Pull-To-Refresh超簡單實現自定義動畫(二)
前言 接上一篇部落格,Ultra-Pull-To-Refresh超簡單終極實現下拉重新整理、上拉載入 入門實現(一) 上一篇是實現了簡單的自帶的重新整理和載入功能。但是這樣簡單的動畫往往不能滿足我們的需求,所以這一片用一個京東重新整理的例子來實現自定義,就
java實現自定義排序(實現Comparable介面)
排序的演算法,大家都不陌生,有氣泡排序、選擇排序、插入排序,快速排序等等。如果現在有學生類,學校添加了很多學生,要你給學生按照學號大小排序,你會怎麼排? 學生類 Student {int stuID; String stuName; int sc
Spark2.x 如何實現自定義排序(利用元組,類--隱式轉換Ordering,Ordered等實現)
交流QQ: 824203453 需求: 對於有複雜排序條件的需求,可以利用自定義排序來實現,同時可以使用多種方案實現自定義排序需求。 對指定的資料(欄位分別為:名稱 年齡 顏值,資料以空格分割),按照指定的要求排序,排序要求為:根據顏值降序,如果顏值
TypeScript 總實現自定義事件(Event)
TypeScript有更好的面向物件性,實現事件只要繼承一個類EventEmitter 定義引發事件 //引入EventEmi
死磕Spring之IoC篇 - 解析自定義標籤(XML 檔案)
> 該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 [Spring 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring 版
自定義監控(網頁報警,郵件報警)
zabbix註:zabbix的安裝配置參考另一篇文章1.Zabbix客戶端配置[[email protected]/* */ ~]# cat /tmp/user.sh #!/bin/bashuptime |awk ‘{print $4}‘[[email protected]/* */
js將一串隨機數字每四位加一個自定義符號(格式:1234-5678-90)
方法一: let string = '1234567890',result = '', index = 0; for(let i=0; i<string.length; i++){ result +=
自定義Banner(寫在Fragment裡了)
Fragment類裡新增 Banner package com.example.lenovo.myapplication.Fragment.Fragment; import android.content.Context; import android.os.Bundle; impor
iOS自定義tabbar(沒有tabbar上的黑線)
自定義tabbar相信在很多專案中都要用到。有的時候 還需要那種 不規則的tabbar,例如中間高兩邊底,例如需要新增tabbar的背景圖片等等。這裡 我要介紹一種 自定義tabbar的方法 ,這種方法可以呼叫系統的 hidesBottomBarWhenPushed 方法,很方便的隱藏tab
WPF TabIndex預設樣式修改:去掉預設虛線框、自定義樣式(Button控制元件為例)
去掉Tab選中預設虛線框 Tab鍵切換時,被選控制元件自動存在虛線框,有時候為了介面美觀,這個虛線框就顯得比較麻煩。廢話不多說,下面是方法。 <Window.Resources> <Style x:Key="MeyFocusVisual" TargetType="{
Idea:通過Live Template自定義模板(類註釋、方法註釋)
1.選擇Live Template File-Settings--Editor--Live Template 2.建立自定義Template Group 點選右邊的+號,選擇Template Group,輸入名稱,比如user  
ListView與自定義介面卡(顯示java端的資料)
一、自定義介面卡 1、BaseAdapter:是所有介面卡類的父類,可以對列表項進行最大限度的定製 2、自定義介面卡中的方法 @Override public int getCount() {//從java端獲取到多少條資料 return da
利用C語言簡單的實現庫存管理系統(本地檔案生成形式儲存)
問題描述: 庫存管理系統是廠家、商城、商場等的管理商品資訊不可缺少的部分,利用這個系統,可以更好地管理商品的資訊。 本系統的功能: 新增、修改、刪除庫存商品資訊,商品資訊包括:編號、名稱、類別、數量、產地等; 進貨或售出商品後,相應的庫存商品數量應該有所改
SparkSQL 使用者自定義函式(UDF、UDAF、開窗)
UDF: 操作單個數據行,產生單個數據行; UDAF: 操作多個數據行,產生一個數據行。 UDTF: 操作一個數據行,產生多個數據行一個表作為輸出。 UDF函式 通過spark.udf.register(“funcName”, func) 來進行註冊 使用:se