Home  >  Article  >  Java  >  How to implement operation logging elegantly in Java SpringBoot project?

How to implement operation logging elegantly in Java SpringBoot project?

WBOY
WBOYforward
2023-04-22 12:55:171129browse

    1. What is AOP?

    AOP (Aspect-Oriented Programming: Aspect-Oriented Programming), speaking of AOP, almost everyone who has studied the Spring framework knows that it is one of the three core ideas of Spring (IOC: Inversion of Control) , DI: dependency injection, AOP: aspect-oriented programming). It can encapsulate logic or responsibilities (such as transaction processing, log management, permission control, etc.) that are not related to the business but are commonly called by the business modules, so as to reduce the duplication of code in the system and reduce the coupling between modules. And it is conducive to future scalability and maintainability.

    2. What did AOP do?

    To put it simply, AOP mainly does three things:

    • #1. Where to cut in, that is, where non-business code such as logging is placed Which business code is executed.

    • 2. When to cut in, before or after the business code is executed.

    • 3. What to do after switching in, such as permission verification, logging, etc.

    can be understood with a picture:

    How to implement operation logging elegantly in Java SpringBoot project?

    A core term on the picture Description:

    • Pointcut: Pointcut, determines where to cut into the business code (that is, weave into the aspect). Pointcuts are divided into execution mode and annotation mode. Execution mode: You can use path expressions to specify which classes are woven into aspects. Annotation mode: You can specify which annotation-modified code is woven into aspects.

    • Advice: Processing, including processing timing and processing content. Processing content means doing something, such as verifying permissions and recording logs. Processing timing refers to when the processing content is executed, which is divided into pre-processing (that is, before the business code is executed), post-processing (after the business code is executed), etc.

    • Aspect: Aspect, namely Pointcut and Advice.

    • Joint pointJoint point is a point of program execution. For example, the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.

    • Weaving: Weaving is the process of processing content in the target object method through a dynamic proxy.

    3. Implementation steps

    (1) Customize an annotation @Log (2) Create an aspect class, and set the cut point to intercept the method annotated @Log. Intercept the passed parameters and record the log (3) Mark @Log on the interface

    The specific implementation steps are as follows:

    1. Add AOP dependency

    The code is as follows (example):

     <dependency>
       	<groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    2. Customize a log annotation

    Logs generally use pointcut expressions of annotation types. We first create A log annotation. When the spring container scans a method with this annotation, it will be enhanced.

    The code is as follows (example):

    @Target({ ElementType.PARAMETER, ElementType.METHOD }) // 注解放置的目标位置,PARAMETER: 可用在参数上  METHOD:可用在方法级别上
    @Retention(RetentionPolicy.RUNTIME)    // 指明修饰的注解的生存周期  RUNTIME:运行级别保留
    @Documented
    public @interface Log {
    
        /**
         * 模块
         */
        String title() default "";
    
        /**
         * 功能
         */
        public BusinessType businessType() default BusinessType.OTHER;
    
        /**
         * 是否保存请求的参数
         */
        public boolean isSaveRequestData() default true;
    
        /**
         * 是否保存响应的参数
         */
        public boolean isSaveResponseData() default true;
    }

    3. Aspect declaration

    Declare an aspect class and hand it over to the Spring container for management.

    The code is as follows (example):

    @Aspect
    @Component
    @Slf4j
    public class LogAspect {
        @Autowired
        private IXlOperLogService operLogService;
        /**
         * 处理完请求后执行
         * @param joinPoint 切点
         */
        @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
        public void doAfterReturnibng(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
            handleLog(joinPoint, controllerLog, null, jsonResult);
        }
        protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
            try {
                // 获取当前的用户
                JwtUser loginUser = SecurityUtils.getLoginUser();
    
                // 日志记录
                XlOperLog operLog = new XlOperLog();
                operLog.setStatus(0);
                // 请求的IP地址
                String iP = ServletUtil.getClientIP(ServletUtils.getRequest());
                if ("0:0:0:0:0:0:0:1".equals(iP)) {
                    iP = "127.0.0.1";
                }
                operLog.setOperIp(iP);
                operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
                if (loginUser != null) {
                    operLog.setOperName(loginUser.getUsername());
                }
                if (e != null) {
                    operLog.setStatus(1);
                    operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
                }
                // 设置方法名称
                String className = joinPoint.getTarget().getClass().getName();
                String methodName = joinPoint.getSignature().getName();
                operLog.setMethod(className + "." + methodName + "()");
                operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
                operLog.setOperTime(new Date());
                // 处理设置注解上的参数
                getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
                // 保存数据库
                operLogService.save(operLog);
    
            } catch (Exception exp) {
                log.error("异常信息:{}", exp.getMessage());
                exp.printStackTrace();
            }
        }
        /**
         * 获取注解中对方法的描述信息 用于Controller层注解
         * @param log 日志
         * @param operLog 操作日志
         * @throws Exception
         */
        public void getControllerMethodDescription(JoinPoint joinPoint, Log log, XlOperLog operLog, Object jsonResult) throws Exception {
            // 设置操作业务类型
            operLog.setBusinessType(log.businessType().ordinal());
            // 设置标题
            operLog.setTitle(log.title());
            // 是否需要保存request,参数和值
            if (log.isSaveRequestData()) {
                // 设置参数的信息
                setRequestValue(joinPoint, operLog);
            }
            // 是否需要保存response,参数和值
            if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) {
                operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
            }
        }
        /**
         * 获取请求的参数,放到log中
         * @param operLog 操作日志
         * @throws Exception 异常
         */
        private void setRequestValue(JoinPoint joinPoint, XlOperLog operLog) throws Exception {
            String requsetMethod = operLog.getRequestMethod();
            if (HttpMethod.PUT.name().equals(requsetMethod) || HttpMethod.POST.name().equals(requsetMethod)) {
                String parsams = argsArrayToString(joinPoint.getArgs());
                operLog.setOperParam(StringUtils.substring(parsams,0,2000));
            } else {
                Map<?,?> paramsMap = (Map<?,?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
                operLog.setOperParam(StringUtils.substring(paramsMap.toString(),0,2000));
            }
        }
        /**
         * 参数拼装
         */
        private String argsArrayToString(Object[] paramsArray) {
            String params = "";
            if (paramsArray != null && paramsArray.length > 0) {
                for (Object object : paramsArray) {
                    // 不为空 并且是不需要过滤的 对象
                    if (StringUtils.isNotNull(object) && !isFilterObject(object)) {
                        Object jsonObj = JSON.toJSON(object);
                        params += jsonObj.toString() + " ";
                    }
                }
            }
            return params.trim();
        }
        /**
         * 判断是否需要过滤的对象。
         * @param object 对象信息。
         * @return 如果是需要过滤的对象,则返回true;否则返回false。
         */
        @SuppressWarnings("rawtypes")
        public boolean isFilterObject(final Object object) {
            Class<?> clazz = object.getClass();
            if (clazz.isArray()) {
                return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
            } else if (Collection.class.isAssignableFrom(clazz)) {
                Collection collection = (Collection) object;
                for (Object value : collection) {
                    return value instanceof MultipartFile;
                }
            } else if (Map.class.isAssignableFrom(clazz)) {
                Map map = (Map) object;
                for (Object value : map.entrySet()) {
                    Map.Entry entry = (Map.Entry) value;
                    return entry.getValue() instanceof MultipartFile;
                }
            }
            return object instanceof MultipartFile || object instanceof HttpServletRequest
                    || object instanceof HttpServletResponse || object instanceof BindingResult;
        }
    }

    4. Mark on the interface

    Mark the custom annotation where the operation log needs to be recorded On the interface, the code is as follows (example):

    	@Log(title = "代码生成", businessType = BusinessType.GENCODE)
        @ApiOperation(value = "批量生成代码")
        @GetMapping("/download/batch")
        public void batchGenCode(HttpServletResponse response, String tables) throws IOException {
            String[] tableNames = Convert.toStrArray(tables);
            byte[] data = genTableService.downloadCode(tableNames);
            genCode(response, data);
        }

    5. The effect of implementation

    When the relevant operations are performed, the log will be recorded, and some basic information will be recorded and stored in the data table.

    How to implement operation logging elegantly in Java SpringBoot project?

    The above is the detailed content of How to implement operation logging elegantly in Java SpringBoot project?. For more information, please follow other related articles on the PHP Chinese website!

    Statement:
    This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete