日志的作用
1.故障排查:通过日志可对系统进行实时健康度监控,系统日志记录程序 Syslog 就是为这个目的而设计的。
2.数据分析:通过对业务系统日志进行关联分析,可以掌握业务系统的整体运行情况,并可通过日志进一步掌握用户画像、用户访问地域、用户访问热点资源等信息,从而为业务平台的市场营销、销售策略等提供数据支撑。
3.安全合规审计:根据国家网络安全法等级保护要求,需要对安全设备日志进行集中存储和分析。
4.内网安全监控:很多企业的信息泄露源于内部,使用日志进行用户行为分析以监控内网安全,已成为行业共识。
5.智能运维:随着大数据时代的到来,数据管理和分析方案越来越智能,自动化运维已逐渐普及。机器数据作为智能运维的基础数据,必将发挥越来越重要的作用。
1.Controller层输入输出效率日志
使用AOP切面技术
/**
* controller request and response log
*
* @author ocean.liu
*/
@Component
@Aspect
@Slf4j
public class ControllerLogAop {
@Around("execution(public * com.xx.xxx.controller.*.*(..))")
public Object aroundApi(ProceedingJoinPoint point) throws Throwable {
return writeLog(point);
}
private Object writeLog(ProceedingJoinPoint point) throws Throwable {
String className = point.getTarget().getClass().getName();
String methodName = point.getSignature().getName();
String reqUrl = StringUtils.EMPTY;
String reqMethod = StringUtils.EMPTY;
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
if (sra != null) {
HttpServletRequest request = sra.getRequest();
reqUrl = request.getRequestURL().toString();
reqMethod = request.getMethod();
}
long beginTime = System.currentTimeMillis();
Object result = null;
Exception ex = null;
try {
result = point.proceed();
return result;
} catch (Exception e) {
ex = e;
throw e;
} finally {
long costTime = System.currentTimeMillis() - beginTime;
if (ex != null) {
log.error("[url: {}][method: {}][className: {}][methodName: {}][cost: {} ms][args: {}][error: {}]", reqUrl, reqMethod, className, methodName, costTime, point.getArgs(), ex.getMessage());
} else {
log.info("[url: {}][method: {}][className: {}][methodName: {}][cost: {} ms][args: {}][return: {}]", reqUrl, reqMethod, className, methodName, costTime, point.getArgs(), result);
}
}
}
}
2.Controller层异常捕获与统一返回处理
2.1)定义业务异常枚举类
public enum ErrorMsg {
COMMON_ERROR_1("服务端异常", -1),
COMMON_ERROR_2("参数校验异常", -2),
BUSSINESS_ERROR_1000001("异常信息", 1000001),
;
private final String msg;
private final Integer code;
ErrorMsg(String msg, Integer code) {
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public Integer getCode() {
return code;
}
}
2.2)定义业务异常
public class BusinessException extends RuntimeException {
private ErrorMsg errorMsg;
public BusinessException(ErrorMsg errorMsg) {
super(errorMsg.getMsg());
this.errorMsg = errorMsg;
}
public ErrorMsg getErrorMsg() {
return errorMsg;
}
}
2.3)添加异常处理类【@RestControllerAdvice】
@RestControllerAdvice
@Slf4j
public class ControllerExceptionHandler {
/**
* 参数校验异常
*
* @param e
* @return
*/
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
ResultVO methodArgumentNotValidExceptionHandle(MethodArgumentNotValidException e) {
log.error("{} = {}", ErrorMsg.COMMON_ERROR_2.getMsg(), e.getMessage());
return ResultVO.error(ErrorMsg.COMMON_ERROR_2.getCode(), e.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(",")));
}
/**
* 业务异常
* @param e
* @return
*/
@ExceptionHandler(value = {BusinessException.class})
ResultVO businessExceptionHandle(BusinessException e) {
log.error(e.getErrorMsg().getMsg(), e);
return ResultVO.error(e.getErrorMsg());
}
@ExceptionHandler(value = {Exception.class})
ResultVO exceptionHandle(Exception e) {
log.error("{} = {}", ErrorMsg.COMMON_ERROR_1.getMsg(), e.getMessage(), e);
return ResultVO.error(ErrorMsg.COMMON_ERROR_1);
}
}
3.日志追踪之链路信息
使用MDC添加链路追踪ID,本案例中使用TID代表一次请求处理的事务ID。
/**
* MDC日志自定义实现过滤器
*
* @author ocean.liu
*/
@Component
public class LogMdcFilter implements Filter {
private static final String MDC_TID_FIELD = "TID";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
MDC.put(MDC_TID_FIELD, UUID.randomUUID().toString());
chain.doFilter(request, response);
} finally {
MDC.remove(MDC_TID_FIELD);
}
}
}