统一日志格式
1.日志对象
统一日志格式方便查看定位问题又方便统计收集。定义一个LogObject对象,里面定义日志的各个字段。例如:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
public class LogObject {
@JsonProperty(index = 1)
private String eventName;//事件名称,一般就是业务方法名称
@JsonProperty(index = 2)
private String traceId; //调用链id,多个服务间传递此ID,可以通过traceId查询所有日志
@JsonProperty(index = 3)
private String msg; //消息内容
@JsonProperty(index = 4)
private long costTime; //接口响应时间
@JsonProperty(index = 6)
private Integer userId; //用户id
@JsonProperty(index = 7)
private Object others; //其他业务参数
@JsonProperty(index = 8)
private Object request; //接口请求入参
@JsonProperty(index = 9)
private Object response; //接口返回值
public Integer getUserId() {
return userId;
}
public LogObject setUserId(Integer userId) {
this.userId = userId;
return this;
}
public Object getRequest() {
return request;
}
public LogObject setRequest(Object request) {
this.request = request;
return this;
}
//......
使用链式的风格,方便设置字段的值:
long endTime = System.currentTimeMillis();
LogObject logObject = new LogObject();
logObject.setEventName(methodName)
.setMsg(msg)
.setTraceId(traceId)
.setUserId(userId)
.setRequest(orderReqDto)
.setResponse(response)
.setCostTime((endTime - beginTime));
LOGGER.info(JSON.toJSONString(logObject));
2.实现方案
2.1.业务方法的 try-catch-finally中加入日志
//示例代码
public String sayHello(String name){
long beginTime = System.currentTimeMillis();
LogObject logObject = new LogObject();
try {
//业务逻辑代码
logObject.setMsg("200");
}catch (Exception e){
logObject.setMsg("500");
}finally {
//统一日志收集代码
logObject.setEventName("sayHello")
.setTraceId("sayHello" + "_" + beginTime)
.setUserId(userId)
.setRequest("name:" + name)
.setResponse("response")
.setCostTime(System.currentTimeMillis() - beginTime);
}
return "hello_" + name;
}
2.2.aop中拦截,但是会影响一点性能
#1引入AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
#2.添加AOP配置,监听环绕通知
@Aspect
@Component
public class LogAspect {
/**
* 环绕通知需要携带ProceedingJoinPoint类型的参数
* 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法
* @return
*/
@Around("execution(* com.zypcy.first1.controller.*.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjp) {
long beginTime = System.currentTimeMillis();
ResponseData responseData = new ResponseData();
LogObject logObject = new LogObject();
Object result = null;
String methodName = pjp.getSignature().getName();
//执行目标方法
try {
//前置通知
result = pjp.proceed();
responseData.setSuccess(true).setCode(ResponseCodeEnum.SUCCESS).setData(result);
} catch (Throwable e) {
//异常通知
responseData.setSuccess(false).setCode(ResponseCodeEnum.FAIL).setMsg(e.getMessage());
}finally {
logObject.setEventName(methodName)
.setMsg(responseData.getCode())
.setTraceId(methodName + "_" + beginTime)
.setUserId(1)
.setRequest(JSON.toJSONString(pjp.getArgs()))
.setResponse(JSON.toJSONString(responseData))
.setCostTime(System.currentTimeMillis() - beginTime);
}
//后置通知
System.out.println("The method " + methodName + " exec ends , logObject : " + JSON.toJSONString(logObject));
return result;
}
}
源码下载
还没有评论,来说两句吧...