SpringAOP . 切面, 是 Spring 得一大特性, 使用目前是使用得面还很窄, 用气对 Controller 层做日志管理, 其实还可以做参数校验和 RSA 校验等一系列前置操作.
在所有 Controller 得每一个方法里面做请求日志记录, 会让代码变得很臃肿和阅读得低效.
没有使用统一请求日志记录得时候, 我记录 Controller 的日志十分痛苦:
- @RestController
- @RequestMapping("gua")
- public class GuaController {
- private Logger logger = LoggerFactory.getLogger(GuaController.class);
- @GetMapping("str")
- public ResponseData str() {
- logger.info("Request 请求日志: 请求控制器, 请求地址 , 请求方法, 请求返回值...... 还有很多需要记录的细节, 为了追溯...");
- return ResponseDataUtil.buildSuccess("Result String");
- }
- @GetMapping("data")
- public ResponseData data() {
- logger.info("Request 请求日志: 请求控制器, 请求地址 , 请求方法, 请求返回值...... 还有很多需要记录的细节, 为了追溯...");
- return ResponseDataUtil.buildSuccess(new User());
- }
- @GetMapping("map")
- public ResponseData map() {
- logger.info("Request 请求日志: 请求控制器, 请求地址 , 请求方法, 请求返回值...... 还有很多需要记录的细节, 为了追溯...");
- HashMap<String, Object> map = new HashMap<>(1);
- map.put("Result", "Map");
- return ResponseDataUtil.buildSuccess(map);
- }
- }
现在只有三个映射, 要是有更多呢? 全都进行 CV 编程吗? 日志变得杂乱不堪, 如果你说不需要记录, 那么业务出现细微差错或者需要追溯源头这种情况出现, 就会让自己变得束手无策. 日志得记录, 是为了辅助我们进行程序执行追溯得. 但是日志记录得杂乱无章, 让程序越来乱.
我是用 AOP 做日志记录, 在执行映射方法之前进行数据纪记录, 在执行完成以后进行返回数据记录, 然后再统一输出.
下面是我得 AOP 代码, 非常简单:
- @Component
- @Aspect
- public class RequestLogAspect {
- private final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class);
- /**
- * 定义切点
- */
- @Pointcut("execution(* com.dong.gua.web.controller..*(..))")
- public void requestServer() {
- }
- @Around("requestServer()")
- public Object doAround(ProceedingJoinPoint pjp) {
- // 记录请求开始执行时间:
- long beginTime = System.currentTimeMillis();
- // 获取请求信息
- ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- HttpServletRequest request = sra.getRequest();
- // 获取代理地址, 请求地址, 请求类名, 方法名
- String remoteAddress = IPUtils.getProxyIP(request);
- String requestURI = request.getRequestURI();
- String methodName = pjp.getSignature().getName();
- String clazzName = pjp.getTarget().getClass().getSimpleName();
- // 获取请求参数:
- MethodSignature ms = (MethodSignature) pjp.getSignature();
- // 获取请求参数类型
- String[] parameterNames = ms.getParameterNames();
- // 获取请求参数值
- Object[] parameterValues = pjp.getArgs();
- StringBuilder sb = new StringBuilder();
- // 组合请求参数, 进行日志打印
- if (parameterNames != null && parameterNames.length> 0) {
- for (int i = 0; i <parameterNames.length; i++) {
- if (parameterNames[i].equals("bindingResult")) {
- break;
- }
- if ((parameterValues[i] instanceof HttpServletRequest) || (parameterValues[i] instanceof HttpServletResponse)) {
- sb.
- append("[").
- append(parameterNames[i]).append("=").append(parameterValues[i])
- .append("]");
- } else {
- sb.
- append("[").
- append(parameterNames[i]).append("=")
- .append(JSON.toJSONString(parameterValues[i], SerializerFeature.WriteDateUseDateFormat))
- .append("]");
- }
- }
- }
- Object result = null;
- try {
- result = pjp.proceed();
- } catch (Throwable throwable) {
- // 请求操纵失败
- // 记录错误日志
- logger.error("(•̀_•́) (っ •̀ω•́) っ 切面处理请求错误! IP 信息 (•̀_•́)->: [{}}]" +
- "URI 信息 (•̀_•́)->:[{}] 请求映射控制类 (•̀_•́)->:[{}]" +
- "请求方法 (•̀_•́)->:[{}] 请求参数列表 (•̀_•́)->:[{}]", remoteAddress, requestURI, clazzName, methodName,
- sb.toString());
- throw throwable;
- }
- // 请求操作成功
- String resultJosnString = "";
- if (result != null) {
- if (result instanceof ResponseData) {
- resultJosnString = JSON.toJSONString(result, SerializerFeature.WriteDateUseDateFormat);
- } else {
- resultJosnString = String.valueOf(result);
- }
- }
- // 记录请求完成执行时间:
- long endTime = System.currentTimeMillis();
- long usedTime = endTime - beginTime;
- // 记录日志
- logger.info("请求操作成功! 请求耗时:[{}]" +
- "IP 信息 ('`)ノ゙->: [{}}] URI 信息 ('`)ノ゙->:[{}]" +
- "请求映射控制类 ('`)ノ゙->:[{}] 请求方法 ('`)ノ゙->:[{}]" +
- "请求参数列表 ('`)ノ゙->:[{}] 返回值 ('`)ノ゙->:[{}]", usedTime, remoteAddress, requestURI, clazzName,
- methodName, sb.toString(), resultJosnString);
- return result;
- }
编写了 AOP 以后, Controller 的代码是这样的:
- @RestController
- @RequestMapping("gua")
- public class GuaController {
- private Logger logger = LoggerFactory.getLogger(GuaController.class);
- @GetMapping("str")
- public ResponseData str() {
- return ResponseDataUtil.buildSuccess("Result String");
- }
- @GetMapping("data")
- public ResponseData data() {
- return ResponseDataUtil.buildSuccess(new User());
- }
- @GetMapping("map")
- public ResponseData map() {
- HashMap<String, Object> map = new HashMap<>(1);
- map.put("Result", "Map");
- return ResponseDataUtil.buildSuccess(map);
- }
- }
干净, 整洁, 易读.
其请求日志是这样的:
请求操作成功! 请求耗时:[137] IP 信息 ('`)ノ゙->: [Client IP: 0:0:0:0:0:0:0:1, fromSource: request.getRemoteAddr}] URI 信息 ('`)ノ゙->:[/gua/str] 请求映射控制类 ('`)ノ゙->:[GuaController] 请求方法 ('`)ノ゙->:[str] 请求参数列表 ('`)ノ゙->:[] 返回值 ('`)ノ゙->:[{"code":"0000","msg":"Result String"}]
感兴趣的小伙伴, 快去试试吧!
来源: https://yq.aliyun.com/articles/688919