2019獨角獸企業重金招聘Python工程師標準>>>
由于最近用阿里云日志服務整合log4j,在配置com.aliyun.openservices.log.log4j.LoghubAppender需要設置一些參數,因為項目中有統一的配置文件,所以想要可以直接在log4j.xml中通過${}來獲取參數,參考下面的配置。
<appender name="AliyunSLS" class="com.aliyun.openservices.log.log4j.LoghubAppender"><param name="projectName" value="${Props.Log4j.AliyunSLS.projectName}"/><!-- 日志服務的project名,必選參數 --><param name="logstore" value="${Props.Log4j.AliyunSLS.logstore}"/><!-- 日志服務的logstore名,必選參數 --><param name="endpoint" value="${Props.Log4j.AliyunSLS.endpoint}"/><!-- 日志服務的http地址,必選參數 --><param name="accessKeyId" value="${Props.Log4j.AliyunSLS.accessKeyId}"/><!-- 用戶身份標識,必選參數 --><param name="accessKey" value="${Props.Log4j.AliyunSLS.accessKey}"/><!-- 用戶身份標識,必選參數 --><param name="packageTimeoutInMS" value="${Props.Log4j.AliyunSLS.packageTimeoutInMS}"/><!-- 被緩存起來的日志的發送超時時間,如果緩存超時,則會被立即發送,單位是毫秒,默認值為3000,最小值為10,可選參數 --><param name="logsCountPerPackage" value="${Props.Log4j.AliyunSLS.logsCountPerPackage}"/><!-- 每個緩存的日志包中包含日志數量的最大值,不能超過 4096,可選參數 --><param name="logsBytesPerPackage" value="${Props.Log4j.AliyunSLS.logsBytesPerPackage}"/><!-- 每個緩存的日志包的大小的上限,不能超過 3MB,單位是字節,可選參數 --><param name="memPoolSizeInByte" value="${Props.Log4j.AliyunSLS.memPoolSizeInByte}"/><!-- Appender 實例可以使用的內存的上限,單位是字節,默認是 100MB,可選參數 --><param name="maxIOThreadSizeInPool" value="${Props.Log4j.AliyunSLS.maxIOThreadSizeInPool}"/><!-- 指定I/O線程池最大線程數量,主要用于發送數據到日志服務,默認是8,可選參數 --><param name="retryTimes" value="${Props.Log4j.AliyunSLS.retryTimes}"/><!-- 指定發送失敗時重試的次數,如果超過該值,會把失敗信息通過log4j的LogLog進行輸出,默認是3,可選參數 --><param name="topic" value="${Props.Log4j.AliyunSLS.topic}"/><!-- 指定日志主題,可選參數 --><!--<param name="source" value="Props.Log4j.AliyunSLS.source"/>--><!-- 指定日志來源,可選參數 --><param name="timeFormat" value="${Props.Log4j.AliyunSLS.timeFormat}"/><!-- 設置時間格式,可選參數 --><param name="timeZone" value="${Props.Log4j.AliyunSLS.timeZone}"/><!-- 設置時區,可選參數 --><param name="Threshold" value="${Props.Log4j.AliyunSLS.Threshold}"/><!-- 輸出WARN級別及以上的消息 --></appender>
log4j.xml中通過${}引用的變量是系統變量,即通過System.getProperty()獲取,顯然這里我們只需要將我們需要的變量設置到系統變量中即可,第一種方法就是在啟動的時候指定系統變量,這種方法不方便,每次啟動都要手動輸入變量的值。第二種就是我們只要確保在日志服務啟動前將參數設置到系統變量中就可以了。這里介紹一種通過自定義LoggerFactory的方式來實現。
通過查看源碼知道,log4j在解析log4j.xml的時候最先解析的是<loggerFactory/>節點,該節點代表的是日志生成工廠接口org.apache.log4j.spi.LoggerFactory,代碼如下:
package org.apache.log4j.spi;import org.apache.log4j.Logger;public interface LoggerFactory {Logger makeNewLoggerInstance(String var1);
}
默認情況下,log4j會加載LoggerFactory的默認實現類org.apache.log4j.DefaultCategoryFactory,代碼如下:
package org.apache.log4j;import org.apache.log4j.spi.LoggerFactory;class DefaultCategoryFactory implements LoggerFactory {DefaultCategoryFactory() {}public Logger makeNewLoggerInstance(String name) {return new Logger(name);}
}
DefaultCategoryFactory.makeNewLoggerInstance(name)直接生成Logger,需要注意的是Logger的構造方法是受保護的,也就是我們沒辦法在其他包中通過new?Logger(name)來生成一個Logger實例,代碼如下:
public class Logger extends Category {private static final String FQCN;protected Logger(String name) {super(name);}...
}
由源碼可以看出,LoggerFactory的實現非常簡單,接下來看一下如何實現。
?
1、由于Logger的構造方法是受保護的,所以我們只要通過自定義Logger子類,就可以使用Logger的功能了
public class Log4jLogger extends Logger {public Log4jLogger(String name) {super(name);}
}
2、實現LoggerFactory接口
public class Log4jLoggerFactory implements LoggerFactory {private static final String LOG4J_PROPERTY_PREFIX_KEY = "log4jPropertyPrefix";//默認以Props.Log4j.開頭的參數加載到系統變量中private static final String DEFAULT_LOG4J_PROPERTY_PREFIX = "Props.Log4j.";static {loadLog4jPropsToSystem();}//加載參數到系統變量中//PropertiesUtils由系統自定義的獲取配置文件的工具類,根據自己的需要自行實現private static void loadLog4jPropsToSystem() {//如果配置了log4jPropertyPrefix,則以該參數作為參數開頭標識String prefixsParam = PropertiesUtils.getStringValue(LOG4J_PROPERTY_PREFIX_KEY);String[] prefixs = null;if (StringUtil.isNotBlank(prefixsParam)) {prefixs = prefixsParam.split(",");} else {prefixs = new String[] {DEFAULT_LOG4J_PROPERTY_PREFIX};}//獲取所有系統配置文件信息Map<Object, Object> allProperties = PropertiesUtils.getAllProperties();if (allProperties != null && !allProperties.isEmpty()) {for (Map.Entry<Object, Object> e : allProperties.entrySet()) {String key = e.getKey().toString();for (String pf : prefixs) {if (key.startsWith(pf)) {System.setProperty(key, e.getValue().toString());System.out.println("Set log4j property[" + key + "=" + e.getValue().toString() + "] into System. ");break;}}}}}@Overridepublic Logger makeNewLoggerInstance(String name) {return new Log4jLogger(name);}}
3、在log4j.xml中配置自定義LoggerFactory,<loggerFactory/>配置在最前面
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'><!-- 自定義log4j日志生成工廠,可以加載配置文件中參數設置到系統參數中,默認加載以Props.Log4j.開頭的參數,可以通過log4jPropertyPrefix配置加載哪些參數,有多個的話用逗號分隔 --><loggerFactory class="自己的包路徑.Log4jLoggerFactory"/>...
</log4j:configuration>
到這里就完畢了,配置文件中的參數就可以給log4j.xml使用了。