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使用了。