Springboot入门之线上日志处理
日志的作用
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";@Overridepublic 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);}}}