JAVA-日志接口处理

我们项目中可能有这种需求,每个人请求了哪些接口?做了什么事情?参数是什么?重要的接口我们需要记日志以便查找。我们不可能在每个接口中去一一处理,可以借助Spring提供的AOP能力+自定义注解轻松应对。

首先我们定义用于Controller方法的注解,使用了此注解的就记录日志。


 
 
  1. /**
  2. * 添加了此注解就记录日志
  3. * @author xiongshiyan at 2018/11/2 , contact me with email yanshixiong@126.com or phone 15208384257
  4. */
  5. @Target({ElementType.METHOD})
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Documented
  8. public @ interface LogOption {
  9. String desc() default "";
  10. String[] exclude() default {}; //排序某些字段
  11. /**
  12. * 参数的key
  13. */
  14. String[] key() default {};
  15. /**
  16. *
  17. * 参数key对应的展示的字段,主要的目的是为了显示的时候的可读性
  18. */
  19. String[] val() default {};
  20. }

其中desc是描述,exclude排除参数的某些字段,key-val将参数可读化。

然后定义AOP切面


 
 
  1. /**
  2. * @author xiongshiyan
  3. * 统一保存操作日志
  4. */
  5. @Aspect
  6. @Component
  7. @Order( 2)
  8. @ConditionalOnProperty(prefix = "spring.log.detail" , name = "enable" , havingValue = "true")
  9. public class WebLogDetailAspect {
  10. @Pointcut( "execution(public * cn.palmte.anfang.controller.alarm..*.*(..))")
  11. public void webLog(){}
  12. @Around(value = "webLog()")
  13. public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  14. Object proceed = proceedingJoinPoint.proceed();
  15. Map<String , Object> map = new HashMap<>( 2);
  16. map.put( "log" , getLog());
  17. map.put( "proceedingJoinPoint" , proceedingJoinPoint);
  18. //发起异步任务,保存日志
  19. SpringContextHolder.getApplicationContext().publishEvent( new SaveLogDetailEvent( map));
  20. return proceed;
  21. }
  22. private Log getLog() {
  23. //因为是异步操作,所以RequestContextHolder.getRequestAttributes()必须放到此处,而不能放到Listener中。
  24. // log中从header中取的属性只能放到这
  25. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  26. HttpServletRequest request = attributes.getRequest();
  27. Log log = new Log();
  28. try {
  29. String name = request.getHeader( "name");
  30. //header中无法传输中文
  31. log.setOperator(null == name ? null : URLDecoder.decode(name, "UTF-8"));
  32. log.setOperatorId(request.getHeader( "userId"));
  33. log.setOperatorPort( "" + request.getRemotePort());
  34. // log.setOperatorIp(request.getRemoteAddr());
  35. //前端转发的通过此header获取
  36. log.setOperatorIp(request.getHeader( "X-FORWARDED-FOR"));
  37. } catch (UnsupportedEncodingException e) {
  38. e.printStackTrace();
  39. }
  40. return log;
  41. }
  42. }

使用SpringBoot的条件注解可以轻松开启或者关闭这个功能,因为一般测试环境是不需要记录的。又由于是就日志,最好不要阻塞正常的调用了,所以利用Spring的事件系统异步化。

然后是参数的解析:


 
 
  1. @Component
  2. public class SaveLogDetailListener {
  3. @Autowired
  4. private SaveLogDetailService saveLogDetailService;
  5. private static final Logger logger = LoggerFactory.getLogger(SaveLogDetailListener.class);
  6. @EventListener
  7. @Async
  8. public void saveLogDetail(SaveLogDetailEvent saveLogDetailEvent){
  9. try {
  10. Map< String , Object> map = ( Map< String, Object>) saveLogDetailEvent.getSource();
  11. log2Db(
  12. (ProceedingJoinPoint) map.get( "proceedingJoinPoint") ,
  13. (Log)map.get( "log"));
  14. } catch (Exception e) {
  15. logger.error( "保存日志失败" , e);
  16. }
  17. }
  18. public void log2Db(ProceedingJoinPoint proceedingJoinPoint , Log log) throws Exception {
  19. Method method = AnnotationUtil.getMethod(proceedingJoinPoint);
  20. LogOption logOption = AnnotationUtil.getLogOption(method);
  21. //没有注解标记的什么都不干
  22. if ( null == logOption) {
  23. return;
  24. }
  25. String[] key = logOption.key();
  26. String[] val = logOption.val();
  27. if (key.length != val.length) {
  28. throw new RuntimeException( "注解标识错误,key和val的长度必须一致");
  29. }
  30. Parameter[] params = AnnotationUtil.getParam(method);
  31. //没有参数的不需要记录
  32. if ( null == params) {
  33. return;
  34. }
  35. Object[] args = proceedingJoinPoint.getArgs();
  36. Map< String, Object> paramMap = convertMap(key, val, params, args , logOption.exclude());
  37. saveLog(logOption, paramMap , log);
  38. }
  39. private void saveLog(LogOption logOption, Map< String, Object> paramMap , Log log) throws UnsupportedEncodingException {
  40. LogMenu menu = logOption.menu();
  41. LogType logType = logOption.type();
  42. log.setCreatedTime( new Date());
  43. log.setMenuCode(menu.name());
  44. log.setMenu(menu.getDesc());
  45. log.setLogTypeCode(logType.name());
  46. log.setLogType(logType.getDesc());
  47. log.setLogDesc(logOption.desc());
  48. Set<LogKv> kvs = new HashSet<>(paramMap.size());
  49. paramMap.forEach((k, v) -> kvs.add( new LogKv(k, String.valueOf(v))));
  50. logService.saveLogAndDetail(log, kvs);
  51. }
  52. private Map< String, Object> convertMap( String[] key, String[] val, Parameter[] params, Object[] args , String[] exclude) {
  53. /**
  54. * 保存所有参数的key和value,包括如果是json实体就拿到json的key和value
  55. */
  56. Map< String, Object> paramMap = getAllParamsMap(params, args , exclude);
  57. /**
  58. * 保存根据注解改变的key的值
  59. */
  60. Map< String, Object> changeMap = new HashMap<>(key.length);
  61. for (int i = 0; i < key.length; i++) {
  62. if (paramMap.containsKey(key[i])) {
  63. Object o = paramMap.remove(key[i]);
  64. changeMap.put(val[i], o);
  65. }
  66. }
  67. //合并
  68. paramMap.putAll(changeMap);
  69. return paramMap;
  70. }
  71. /**
  72. * @param params 参数描述
  73. * @param args 参数值,与params是对应的
  74. */
  75. private Map< String, Object> getAllParamsMap(Parameter[] params, Object[] args , String[] exclude) {
  76. Map< String, Object> map = new HashMap<>(params.length);
  77. for (int i = 0; i < params.length; i++) {
  78. if ( null == args[i]) {
  79. continue;
  80. }
  81. Annotation[] annotations = params[i].getAnnotations();
  82. if ( null == annotations || 0 == annotations.length) {
  83. parse(args[i], i, map);
  84. continue;
  85. }
  86. Annotation annotation = annotations[ 0];
  87. if (annotation instanceof RequestParam) {
  88. RequestParam requestParam = (RequestParam) annotation;
  89. String paramName = paramName(requestParam);
  90. map.put(paramName, args[i]);
  91. }
  92. if (annotation instanceof PathVariable) {
  93. PathVariable pathVariable = (PathVariable) annotation;
  94. String paramName = paramName(pathVariable);
  95. map.put(paramName, args[i]);
  96. }
  97. if (annotation instanceof RequestBody) {
  98. parse(args[i], i, map);
  99. }
  100. }
  101. excludeMapKey(exclude, map);
  102. return map;
  103. }
  104. /**根据 exclude 排除 map 中某些字段*/
  105. private void excludeMapKey( String[] exclude, Map< String, Object> map) {
  106. if( null != exclude && exclude.length > 0){
  107. for( String e : exclude){
  108. if(map.containsKey(e)){
  109. map.remove(e);
  110. }
  111. }
  112. }
  113. }
  114. private String paramName(PathVariable pathVariable) {
  115. String value = pathVariable.value();
  116. //因为@PathVariable的value和name互为别名的缘故
  117. return "".equals(value) ? pathVariable.name() : value;
  118. }
  119. private String paramName(RequestParam requestParam) {
  120. String value = requestParam.value();
  121. //因为@RequestParam的value和name互为别名的缘故
  122. return "".equals(value) ? requestParam.name() : value;
  123. }
  124. private void parse( Object argParam, int i, Map< String, Object> map) {
  125. String arg = String.valueOf(argParam);
  126. boolean jsonObject = JsonUtil.isJsonObject(arg);
  127. if (!jsonObject) {
  128. map.put( "param-" + i, arg);
  129. } else {
  130. map.putAll(json2Map(arg));
  131. }
  132. }
  133. /**
  134. * 前端传输过来的json数据转换成map
  135. */
  136. private Map< String, Object> json2Map( String json) {
  137. return new JSONObject(json).unwrap().getInnerMap();
  138. }
  139. }

 
 
  1. /**
  2. * @author xiongshiyan at 2018/11/2 , contact me with email yanshixiong@126.com or phone 15208384257
  3. */
  4. public class AnnotationUtil {
  5. /**
  6. * 通过目标对象获取方法实例
  7. */
  8. public static Method getMethod(JoinPoint joinPoint) {
  9. Object target = joinPoint.getTarget();
  10. //获取方法签名
  11. Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
  12. try {
  13. method = target.getClass().getMethod(method.getName(), method.getParameterTypes());
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. return method;
  18. }
  19. /**
  20. * 获取方法的参数
  21. */
  22. public static Parameter[] getParam(Method method){
  23. if( null == method){
  24. return new Parameter[ 0];
  25. }
  26. return method.getParameters();
  27. }
  28. /**
  29. * 获取注解
  30. */
  31. public static LogOption getLogOption(Method method) {
  32. if( null == method){
  33. return null;
  34. }
  35. return method.isAnnotationPresent(LogOption.class) ? method.getAnnotation(LogOption.class) : null;
  36. }
  37. }

 

转载自:https://blog.csdn.net/xxssyyyyssxx/article/details/84572517
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值