在系统中经常会加很多必要的日志来帮忙我们定位分析解决问题,虽然这个日志挺简单,但是真的很重要,下面从几个方面来总结下
1.日志框架的来源
java有很多现成的日志框架,我们可以借鉴学习源码和设计思想,可参考之前转载的文章
2.日志打印规范
2.1日志分级:像log4j2有8个日志级别,按照从低到高为:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF。我们常用的就是日志级别分为info ,warn,error, info和debug一般主要用在方法调用开始,参数校验,方法结束等场景,error使用场景:参数校验出错还有业务异常,rpc接口异常等场景 warn使用场景:校验参数以及警告等业务场景
2.2生产环境约束:生产环境严禁使用ConsoleAppender(具体参考该篇博文);强烈推荐AsyncLogger,也不能出现System.out.println()语句
2.3 日志输出格式建议统一
控制台:%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%t] %c:%L - %m%n
error:%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] [%t] %l - %m%n
info:%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %C.%M - %m%n
2.4规范日志格式: 可以搞一个日志打印工具类,如下代码示例
public class TradeLog {
private String methodName;
private String methodValue;
private Map<String,Object> paramInfo;
public TradeLog(String methodName, String methodValue, Map<String,Object> paramInfo) {
this.methodName = methodName;
this.methodValue = methodValue;
this.paramInfo=paramInfo;
}
@Override
public String toString() {
return "TradeLog{" +
"methodName='" + methodName + '\'' +
", methodValue='" + methodValue + '\'' +
", paramInfo=" + paramInfo +
'}';
}
public static TradeLog.TradeLogBuilder builder(String logKey, String logValue) {
return new TradeLog.TradeLogBuilder(logKey,logValue);
}
public static class TradeLogBuilder {
private String methodName;
private String methodValue;
private Map<String,Object> paramInfo;
public TradeLogBuilder(String methodName, String methodValue) {
this.methodName = methodName;
this.methodValue = methodValue;
}
public TradeLog.TradeLogBuilder appendParam(String paramName, Object paramValue){
if(paramInfo ==null){
paramInfo= Maps.newHashMap();
}
paramInfo.put(paramName,paramValue);
return this;
}
public TradeLog build() {
return new TradeLog(this.methodName, this.methodValue,this.paramInfo);
}
}
public static void main(String[] args) {
TradeLog tradeLog = TradeLog.builder("getOrderById", "获取用户订单信息").appendParam("name",222).appendParam("orderId",333333).build();
System.out.println(tradeLog);
}
3.集成log4j2
3.1 maven包依赖
#jar包依赖
<!-- log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.0.0</version>
</dependency>
<!-- log4j2 -->
<exclusion>
<groupId>log4j</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>*</artifactId>
</exclusion>
3.2 升级后压测结果分析(1台docker【8核/16G内存/50G磁盘】)
- log4j版本为1.2.16时,TPS为3636.7笔/秒,CPU为66.54%;
- log4j版本为1.2.17时,TPS为3769.92笔/秒,CPU为66.87%;
- log4j版本为2.3时,TPS为5817.41笔/秒,CPU为91.24%;
- log4j版本为2.3混合异步打日志时,TPS为6903.75笔/秒,CPU为96.3%;
- log4j版本为2.3纯异步打日志时,TPS为6913.06笔/秒,CPU为95.6%;
从上面压测结果来看纯异步肯定性能要好,但是升级log4j2还是建议使用混合异步方式,这样能灵活控制error级别log同步输出防止丢失,一些有必要输出的info级别log异步输出提高性能,也就是info级别日志用AsyncLogger异步打,error级别log比较重要还是用Logger同步打。当然还有一种全局方式使用异步的,log4j2的配置文件Loggers中都使用Logger标签即可,在classpath中添加文件“log4j2.component.properties”,文件增加以下内容:“Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector”,这种方式就是所有日志都是异步打印了。