用户操作系统日志 记录用户操作记录日志持久化 springAOP切点实现方式 可以填加用户的任何类型的日志 正常+异常记录
springAOP实现操作日志记录,并记录请求参数与编辑前后字段的具体改变
原文
参考
说明:
直接上效果图
一个成熟的系统,应对用户的某些增删改操作,特别是管理员的增删改操作进行日志持久化处理。这些功能基本包括了用户的操作日志。那么我们要对一个完整的操作记录,其单位就是方法。通过AOP的环绕通知可以把切点的记录在内,得到日志并持久化处理。那么就不废话直接上设计了。
pojo
- import lombok.Data;
- import javax.persistence.*;
- import javax.validation.constraints.NotNull;
- import javax.validation.constraints.Size;
- import java.util.Date;
-
- /**
- * 系统日志实体定义
- *
- */
- @Entity
- @Table(name = "ABC_SYSTEM_LOG")
- @Data
- public class SystemLog
- {
- public static final int STATUS_NORMAL = 0;
- public static final int STATUS_PAUSE = 1;
-
- public static final String DEFAULT_OWNER_TYPE = "SYS";
- public static final String DEFAULT_OWNER = "0";
-
- /**
- * 标识
- */
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- private int id;
- /**
- * 应用端
- */
- @Column(name = "app_name")
- @Size(max = 255)
- private String appName;
- /**
- * 日志类型:0为操作日志,1为异常日志
- */
- @Column(name = "log_type")
- private int logType;
- /**
- * 访问者/请求者
- */
- @Column(name = "user")
- @Size(max = 255)
- private String user;
- /**
- * 账号类型
- */
- @Column(name = "user_type")
- @Size(max = 255)
- private String userType;
- /**
- * 方法名称
- */
- @Column(name = "method_name")
- @Size(max = 255)
- private String methodName;
- /**
- * 请求参数
- */
- @Lob
- @Column(name = "request_params")
- private String requestParams;
- /**
- * 方法描述
- */
- @Column(name="method_description")
- @Size(max = 255)
- private String methodDescription;
- /**
- * 访问ip
- */
- @Column(name="request_ip")
- @Size(max = 255)
- private String requestIp;
- /**
- * 请求URL
- */
- @Column(name="request_uri")
- @Size(max = 255)
- private String requestUri;
- /**
- * 请求PATH
- */
- @Column(name="request_path")
- @Size(max = 255)
- private String requestPath;
- /**
- * 异常码
- */
- @Column(name="exception_code")
- @Size(max = 255)
- private String exceptionCode;
- /**
- * 异常描述
- */
- @Column(name="exception_detail")
- @Size(max = 255)
- private String exceptionDetail;
- /**
- * d
- */
- @Column(name="request_status")
- private int requestStatus;
- /**
- * 备注
- */
- @Lob
- @Column(name = "remark")
- private String remark;
- /**
- * 状态
- */
- @Column(name = "status")
- private int status = STATUS_NORMAL;
- /**
- * 所有者
- */
- @NotNull
- @Size(max = 32)
- @Column(name = "owner", updatable = false)
- private String owner = DEFAULT_OWNER;
- /**
- * 所有者类型
- */
- @Size(max = 32)
- @Column(name = "owner_type", updatable = false)
- private String ownerType = DEFAULT_OWNER_TYPE;
- /**
- * 创建人/时间,更新人/时间
- */
- @Column(name = "creator", nullable = false, updatable = false)
- private String creator;
- @Temporal(TemporalType.TIMESTAMP)
- @Column(name = "create_date", nullable = false, updatable = false)
- private Date creationDate = new Date();
- @Column(name = "modifier", insertable = false, updatable = true)
- private String modifier;
- @Temporal(TemporalType.TIMESTAMP)
- @Column(name = "modify_date", insertable = false, updatable = true)
- private Date modifyDate;
-
- }
注解
- import java.lang.annotation.*;
-
-
- /**
- *自定义注解 拦截Controller
- */
- @Target({ElementType.PARAMETER, ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface SystemControllerLog {
- String description() default "";
- }
切点
- import com.geek.abc.core.systemLog.SystemLog;
- import com.geek.abc.core.systemLog.SystemLogManager;
- import com.geek.abc.core.util.DateHelper;
- import com.jade.bss.organization.account.AdminAccount;
- import com.jade.bss.organization.customer.Customer;
- import com.jade.framework.base.context.ApplicationContextUtils;
- import lombok.extern.slf4j.Slf4j;
- import net.sf.json.JSONObject;
- import org.apache.shiro.SecurityUtils;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.springframework.stereotype.Component;
- import org.springframework.web.context.request.RequestContextHolder;
- import org.springframework.web.context.request.ServletRequestAttributes;
- import javax.servlet.http.HttpServletRequest;
- import java.lang.reflect.Method;
-
- /**
- * 系统日志 切面
- * optimized by_liaoqian 20191212
- */
- @Aspect
- @Component
- @SuppressAjWarnings("all")
- @Slf4j
- public class SystemLogAspect {
-
- private SystemLogManager getSystemLogManager() {
- return ApplicationContextUtils.getBean("bss_systemLog_manager");
- }
-
-
-
- /**
- * Service层切点
- */
- @Pointcut("@annotation(com.geek.abc.core.systemLog.aopHandle.SystemServiceLog)")
- public void serviceAspect() {
- }
-
-
-
- /**
- * Controller层切点
- */
- @Pointcut("@annotation(com.geek.abc.core.systemLog.aopHandle.SystemControllerLog)")
- public void controllerAspect() {
- }
-
-
-
- /**
- * 环绕通知 用于拦截controller层记录用户的操作
- * @param proceeding 切点
- */
- @Around("controllerAspect()")
- @SuppressAjWarnings("all")
- public Object aroundAdvice(ProceedingJoinPoint proceeding) throws Exception {
- Object[] args=proceeding.getArgs();
- Object result=null;
- String userName = "未识别";
- String userType = "未识别";
- String appName = "";
- HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
- if (SecurityUtils.getSubject() != null) {
- if (SecurityUtils.getSubject().getPrincipal() != null) {
- String userClassStr = SecurityUtils.getSubject().getPrincipal().toString();
- if (userClassStr.contains("com.jade.bss.organization.account.AdminAccount")) {
- AdminAccount adminAccount = (AdminAccount) SecurityUtils.getSubject().getPrincipal();
- userName = adminAccount.getName();
- userType = adminAccount.getType();
- } else if (SecurityUtils.getSubject().getPrincipal().toString().contains("com.jade.bss.organization.customer.Customer")) {
- Customer customer = (Customer) SecurityUtils.getSubject().getPrincipal();
- userName = customer.getName();
- userType = customer.getOwnerType();
- }
- }
-
- }
- if (userType.equals("MEMBER")) {
- appName = "client";
- } else {
- appName = "web";
- }
- String params = "";
- System.out.println(proceeding.getArgs());
- if (proceeding.getArgs() != null && proceeding.getArgs().length > 0) {
- for (int i = 0; i < proceeding.getArgs().length; i++) {
- if (proceeding.getArgs()[i] != null) {
- params += proceeding.getArgs()[i].toString() + ";";
- if (proceeding.getArgs()[i].toString().contains("com.geek") || proceeding.getArgs()[i].toString().contains("com.jade.abc")) {
- Object object = proceeding.getArgs()[i];
- JSONObject json = JSONObject.fromObject(object);
- params += json.toString();
- }
- }
- }
- }
-
- SystemLog systemLog = new SystemLog();
- try {
- log.info("=====前置通知开始=====");
- System.out.println("请求方法:" + (proceeding.getTarget().getClass().getName() + "." + proceeding.getSignature().getName() + "()"));
- System.out.println("方法描述:" + getControllerMethodDescription(proceeding));
- System.out.println("请求路径:" + request.getRequestURI());
- System.out.println("请求参数:" + request.getQueryString());
- System.out.println("请求人:" + userName);
- System.out.println("请求人类型:" + userType);
- System.out.println("请求IP:" + request.getRemoteAddr());
- System.out.println("日期:" + DateHelper.getCurDateTime());
-
- // tip:执行连接点的方法 获取返回值 必不可少!!!
- result=proceeding.proceed(args);
-
- systemLog.setAppName(appName);
- systemLog.setCreator(userName);
- systemLog.setLogType(0);
- systemLog.setUser(userName);
- systemLog.setUserType(userType);
- systemLog.setMethodDescription(getControllerMethodDescription(proceeding));
- systemLog.setMethodName(proceeding.getTarget().getClass().getName() + "." + proceeding.getSignature().getName() + "()");
- systemLog.setExceptionCode(null);
- systemLog.setExceptionDetail(null);
- systemLog.setRequestIp(request.getRemoteAddr());
- systemLog.setRequestParams(request.getQueryString());
- systemLog.setRequestPath(request.getServletPath());
- systemLog.setRequestUri(request.getRequestURI());
- systemLog.setRequestStatus(200);
- systemLog.setRemark(params);
-
- // 初次登录,无法识别,所以通过截取参数获取,保留
- if ("用户登录".equals(systemLog.getMethodDescription())
- && "未识别".equals(userName) && "未识别".equals(userType)) {
- String name = request.getQueryString().split("=")[2];
- systemLog.setAppName("client");
- systemLog.setCreator(name);
- systemLog.setUser(name);
- systemLog.setUserType("MEMBER");
- }
-
- log.info("=====后置通知结束=====");
- } catch (Throwable e) {
- log.error("=====异常通知开始=====");
- systemLog.setAppName(appName);
- systemLog.setCreator(userName);
- systemLog.setLogType(1);
- systemLog.setUser(userName);
- systemLog.setUserType(userType);
- systemLog.setMethodDescription(getControllerMethodDescription(proceeding));
- systemLog.setMethodName(proceeding.getTarget().getClass().getName() + "." + proceeding.getSignature().getName() + "()");
- systemLog.setExceptionCode(e.getClass().getName());
- systemLog.setExceptionDetail(e.getMessage());
- systemLog.setRequestIp(request.getRemoteAddr());
- systemLog.setRequestParams(request.getQueryString());
- systemLog.setRequestPath(request.getServletPath());
- systemLog.setRequestUri(request.getRequestURI());
- systemLog.setRequestStatus(500);
- systemLog.setRemark(params);
- log.error("=====异常通知结束=====");
- } finally {
- log.info("=====最终通知开始=====");
- getSystemLogManager().addSystemLog(systemLog);
- }
- return result;
- }
-
-
-
- /**
- * 获取注解中对方法的描述信息 用于service层注解
- *
- * @param joinPoint 切点
- * @return 方法描述
- * @throws Exception
- */
-
- public static String getServiceMthodDescription(JoinPoint joinPoint)
- throws Exception {
- String targetName = joinPoint.getTarget().getClass().getName();
- String methodName = joinPoint.getSignature().getName();
- Object[] arguments = joinPoint.getArgs();
- Class targetClass = Class.forName(targetName);
- Method[] methods = targetClass.getMethods();
- String description = "";
- for (Method method : methods) {
- if (method.getName().equals(methodName)) {
- Class[] clazzs = method.getParameterTypes();
- if (clazzs.length == arguments.length) {
- description = method.getAnnotation(SystemServiceLog.class).description();
- break;
- }
- }
- }
- return description;
- }
-
- /**
- * 获取注解中对方法的描述信息 用于Controller层注解
- *
- * @param joinPoint 切点
- * @return 方法描述
- * @throws Exception
- */
- public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
- String targetName = joinPoint.getTarget().getClass().getName();
- String methodName = joinPoint.getSignature().getName();
- Object[] arguments = joinPoint.getArgs();
- Class targetClass = Class.forName(targetName);
- Method[] methods = targetClass.getMethods();
- String description = "";
- for (Method method : methods) {
- if (method.getName().equals(methodName)) {
- Class[] clazzs = method.getParameterTypes();
- if (clazzs.length == arguments.length) {
- description = method.getAnnotation(SystemControllerLog.class).description();
- break;
- }
- }
- }
- return description;
- }
- }
重点在于切点的环绕通知写法,在切点的aroundAdvice方法内,由于采用的是远古的ssh项目,一些非必要的dao层和service层就没有写出来,那么controller的实现方式也特别简单。
事例
- /**
- * 删除会员
- * @param id
- * @param response
- * @throws Exception
- */
- @RequestMapping(value = "/remove")
- @RequiresAuthentication
- @CheckPermission(permission = "member_remove")
- @SystemControllerLog(description = "删除会员信息")
- public void removeMember(@RequestParam(value = "id") long[] id, HttpServletResponse response) throws Exception {
- try {
- getMemberManager().removeMember( id );
- ResponseUtil.writeSuccessResult( response );
- } catch (BssException e) {
- e.printStackTrace();
- ResponseUtil.writeErrorResult( response, STATUS_ERROR, e.getMessage() );
- }
- }
就是一个简单的注解开发
@SystemControllerLog(description = "方法描述")
那么看一下实际运行图
操作正常下:
操作异常:
日志列表:

浙公网安备 33010602011771号