问题:
系统需要记录用户的关键操作日志,以便后期的系统维护,方便的查看问题,及时排除
分析:
(1)保存字段:作为一个日志记录功能,首先数据库新建一张表保存用户的操作关键字段,
用户名,ip,操作描述,时间,日志id
(2)采用技术:
第一种:新建一个日志业务实现,在操作发生时进行联动
缺点是耦合太紧密,无用代码增多,后期代码臃肿,改动时地方分散,不利于维护
第二种:使用spring 的 aop 技术进行切面切入
由于本身系统结构不规范,参数,方法名没有一致的样式,使用正则匹配方法名不是很方便
本身日志记录也只是记录关键操作,api全部切入的话,就不需要这个功能了
必须有针对性的记录关键操作日志
第三种:使用spring 的 aop 技术切到自定义注解上,针对不同注解标志进行参数解析,记录日志
缺点是要针对每个不同的注解标志进行分别取注解标志,获取参数进行日志记录输出
解决:
由于本身系统的不规范,个人技术能力的欠缺,目前采用第三种办法进行关键日志的记录
(1)首先新建 注解类【MethodLog】
package org.triber.portal.service.logAop; import java.lang.annotation.*; /** * 日志切面注解 */ @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodLog { String remark() default ""; String operType() default "0"; // String desc() default ""; }
(2)新建切面实现类【LogService】
package org.triber.portal.service.logAop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.triber.common.helper.StringUtil; import org.triber.common.helper.UserUtil; import org.triber.common.model.menu.MenuModel; import org.triber.common.model.user.User; import org.triber.portal.dao.mapper.operateLog.OperateLogMapper; import org.triber.portal.model.area.Area; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Calendar; /** * 日志切面实现 */ @Component @Aspect public class LogService { @Autowired private OperateLogMapper dao; public LogService() { System.out.println("Aop"); } /** * 切点 */ @Pointcut("@annotation(org.triber.portal.service.logAop.MethodLog)") public void methodCachePointcut() { } /** * 切面 * * @param point * @return * @throws Throwable */ @Around("methodCachePointcut()") public Object around(ProceedingJoinPoint point) throws Throwable { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder .getRequestAttributes()).getRequest(); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E"); Calendar ca = Calendar.getInstance(); String operDate = df.format(ca.getTime()); String ip = getIp(request); User user = UserUtil.getCurrentUser(request); String loginName; String name; if (user != null) { loginName = user.getLoginName(); } else { loginName = "匿名用户"; } String methodRemark = getMthodRemark(point); String methodName = point.getSignature().getName(); String packages = point.getThis().getClass().getName(); if (packages.indexOf("$$EnhancerByCGLIB$$") > -1) { // 如果是CGLIB动态生成的类 try { packages = packages.substring(0, packages.indexOf("$$")); } catch (Exception ex) { ex.printStackTrace(); } } String operatingcontent = ""; Object[] method_param = null; Object object; try { method_param = point.getArgs(); //获取方法参数 // String param=(String) point.proceed(point.getArgs()); object = point.proceed(); } catch (Exception e) { // 异常处理记录日志..log.error(e); throw e; } Syslog sysLog = new Syslog(); sysLog.setOptId(org.triber.common.util.StringUtil.getRandNum(11)); sysLog.setLoginId(user.getLoginId()); sysLog.setLoginName(loginName); sysLog.setIpAddress(ip); sysLog.setMethodName(packages + "." + methodName); sysLog.setMethodRemark(methodRemark); sysLog.setOptDate(operDate); /** * 用户操作 */ if("新增用户".equals(methodRemark)){ HttpServletRequest req = (HttpServletRequest) method_param[0]; sysLog.setOperatingcontent("新增用户: 用户名为 " + req.getParameter("userName")); } if("删除用户".equals(methodRemark)){ String loginId = (String) method_param[1]; sysLog.setOperatingcontent("删除用户: 用户id为 " + loginId); } if("修改用户".equals(methodRemark)){ HttpServletRequest req = (HttpServletRequest) method_param[0]; sysLog.setOperatingcontent("修改用户: 用户名为 " + req.getParameter("loginName")); } /** * 角色操作 */ if("新增角色".equals(methodRemark)){ HttpServletRequest req = (HttpServletRequest) method_param[0]; sysLog.setOperatingcontent("新增角色: 角色名为 " + req.getParameter("roleDesc")); } if("删除角色".equals(methodRemark)){ String roleId = (String) method_param[1]; sysLog.setOperatingcontent("新增角色: 角色id为 " + roleId); } if("修改角色".equals(methodRemark)){ HttpServletRequest req = (HttpServletRequest) method_param[0]; sysLog.setOperatingcontent("修改角色: 角色名为 " + req.getParameter("roleDesc")); } /** * 菜单操作 */ if("新增菜单".equals(methodRemark)){ MenuModel menuModel = (MenuModel) method_param[0]; sysLog.setOperatingcontent("新增菜单: 菜单名为 " + menuModel.getMenuName()); } if("删除菜单".equals(methodRemark)){ String menuId = (String) method_param[0]; sysLog.setOperatingcontent("删除菜单: 菜单id为 " + menuId); } if("修改菜单".equals(methodRemark)){ MenuModel menuModel = (MenuModel) method_param[0]; sysLog.setOperatingcontent("修改菜单: 菜单名为 " + menuModel.getMenuName()); } /** * 部门操作 */ if("新增部门".equals(methodRemark)){ String deptDesc = (String) method_param[1]; sysLog.setOperatingcontent("新增部门: 部门名为 " + deptDesc); } if("修改部门".equals(methodRemark)){ String deptDesc = (String) method_param[2]; sysLog.setOperatingcontent("修改部门: 部门名为 " + deptDesc); } /** * 地区操作 */ if("新增地区".equals(methodRemark)){ Area area = (Area) method_param[0]; sysLog.setOperatingcontent("新增地区: 地区名为 " + area.getAreaDscr().trim()); } if("删除地区".equals(methodRemark)){ String areaId = (String) method_param[0]; sysLog.setOperatingcontent("删除地区: 地区id为 " + areaId); } if("修改地区".equals(methodRemark)){ Area area = (Area) method_param[0]; sysLog.setOperatingcontent("修改地区: 地区名为 " + area.getAreaDscr().trim()); } /** * 机构操作 */ if("新增机构".equals(methodRemark)){ String isAdd = (String) method_param[1]; if(isAdd.equals("1")){ String addOrgDscrPancy = (String) method_param[5]; sysLog.setOperatingcontent("新增机构: 机构名为 " + addOrgDscrPancy); }else{ String addOrgDscrPancy = (String) method_param[5]; sysLog.setOperatingcontent("修改机构: 机构名为 " + addOrgDscrPancy); } } if("删除机构".equals(methodRemark)){ String orgId = (String) method_param[3]; sysLog.setOperatingcontent("删除机构: 机构id为 " + orgId); } /** * 空参数 */ if("".equals(methodRemark) || null == methodRemark){ sysLog.setOperatingcontent("操作参数: " + method_param[0]); } dao.saveSysLog(sysLog); // System.out.println("日志实体:"+sysLog.getLoginName()+sysLog.getMethodRemark()+sysLog.getOperatingcontent()); return object; } /** * 方法异常时调用 * * @param ex */ public void afterThrowing(Exception ex) { System.out.println("afterThrowing"); System.out.println(ex); } /** * 获取方法中的中文备注 * * @param joinPoint * @return * @throws Exception */ public static String getMthodRemark(ProceedingJoinPoint joinPoint) throws Exception { String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] method = targetClass.getMethods(); String methode = ""; for (Method m : method) { if (m.getName().equals(methodName)) { Class[] tmpCs = m.getParameterTypes(); if (tmpCs.length == arguments.length) { MethodLog methodCache = m.getAnnotation(MethodLog.class); if (methodCache != null) { methode = methodCache.remark(); } break; } } } return methode; } /** * 获取请求ip * * @param request * @return */ public static String getIp(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) { int index = ip.indexOf(","); if (index != -1) { return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip.substring(0, index); } else { return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip; } } ip = request.getHeader("X-Real-IP"); if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) { return ip; } return request.getRemoteAddr().equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip; } }
(3)新建日志实体【Syslog】
package org.triber.portal.service.logAop; import lombok.Getter; import lombok.Setter; import java.io.Serializable; /** * 操作日志实体类 */ public class Syslog implements Serializable{ @Setter@Getter private String optId; @Setter@Getter private String loginId; @Setter@Getter private String loginName; @Setter@Getter private String ipAddress; @Setter@Getter private String methodName; @Setter@Getter private String methodRemark; @Setter@Getter private String operatingcontent; @Setter@Getter private String optDate; //模糊条件 @Setter@Getter private String serchCondition; }
(4)日志切入【添加注解标志即可】
/** * 删除金融机构 * @param request * @param response * @param operateVersionId 机构版本 * @param orgId 机构iD * @param orgLvl 机构层级 * @return * @throws Exception */ @RequestMapping(value = "delOrgByFlagAndOrgId", method = RequestMethod.POST, produces = {"application/json"}) @MethodLog(remark = "删除机构") public String delOrgByFlagAndOrgId(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "operateVersionId", required = false) String operateVersionId, @RequestParam(value = "orgId", required = false) String orgId, @RequestParam(value = "orgLvl", required = false) String orgLvl) throws Exception{ orgService.delOrgByFlagAndOrgId(areaIdPrefix,orgId, orgLvl, operateVersionId); return "0"; }
(5)maven注入aop依赖
<!--spring切面aop依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 在application.properties文件里加这样一条配置 spring.aop.auto=true 在springboot项目里加这两条配置即可,就可以开启aop功能
总结:
至此,大功告成,可以开始加注解标志,获取参数,记录关键信息,输出日志了。。。