java 项目日记实现两种方式:拦截器方式实现日记与定义日记注解方式实现日记

2023-12-22 12:45:51

? ? ? ? 通常只要是java web项目基本都离不开项目日记,项目日记存在的意义很多,例如:安全审计,问题追踪都离不开项目日记。下面我们说一下项目日记实现最常用的两种方式?。

一 拉截器实现项目日记

? ?1 实现一个拦截器基类,用于事件项目的请求日记

? ? ?用拦截器实现记录日记代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.LocalDateTime;

/**
 * @author hua
 * @Description 自定义拦截器,用于记录处理Web请求的基本功能。
 */
public class BaseInterceptor extends HandlerInterceptorAdapter {

    // ThreadLocal 用于在每个线程中存储API消息日志
    ThreadLocal<ApiMessageLog> apiMessageLogThreadLocal = new ThreadLocal<>();

    // 用于记录日志的Logger
    public static final Logger logger = LoggerFactory.getLogger(BaseInterceptor.class);

    /**
     * 在请求完成后调用的方法,用于执行任何必要的清理或日志记录。
     *
     * @param request   HTTP请求对象。
     * @param response  HTTP响应对象。
     * @param handler   处理请求的处理程序(控制器方法)。
     * @param ex        在请求处理过程中发生的任何异常。
     * @throws Exception 如果在完成后处理过程中发生异常。
     */
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        // 获取当前线程的API消息日志
        ApiMessageLog apiMessageLog = apiMessageLogThreadLocal.get();

        // 检查API消息日志是否非空
        if (apiMessageLog != null) {

            // 计算请求所用时间
            long time = Duration.between(apiMessageLog.getCtime(), LocalDateTime.now()).getSeconds();
            apiMessageLog.setTimeLong((int) time);

            // 如果请求时间超过8秒,则记录错误日志
            if (time > 8) {
                logger.error("超时 > " + time + " > " + apiMessageLog.getApiName() + "\n" + apiMessageLog);
            }

            // 如果在请求处理过程中发生异常,将其设置为API消息日志的结果
            if (ex != null) {
                apiMessageLog.setResult(ex.getMessage());
            } else {
                // 如果没有异常,从请求属性中设置结果
                String result = (String) request.getAttribute("_REQ_RESULT");
                if (result != null) {
                    apiMessageLog.setResult(result);
                }

                // 从请求属性中设置API订单号
                String _REQ_ORDER_SN = (String) request.getAttribute("_REQ_ORDER_SN");
                if (_REQ_ORDER_SN != null) {
                    apiMessageLog.setApiOrderNo(_REQ_ORDER_SN);
                }

                // 从请求属性中设置连接器ID
                String _REQ_CONNECTOR_ID = (String) request.getAttribute("_REQ_CONNECTOR_ID");
                if (_REQ_ORDER_SN != null) {
                    apiMessageLog.setConnectorId(_REQ_CONNECTOR_ID);
                }
            }

            // 将API消息日志添加到内存队列,队列批量保存效率更高
            MemDataTable.API_MESSAGE_LOGS.offer(apiMessageLog);
        }
    }
}

继承上面写日记基类拦截器(如果项目简单且不用选择性记录,可以把以下代码合并成一个拦截器即可)。

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;

/**
 * @author hua
 * @Description 统一处理中电联的拦截器
 */
@Component
public class ZdlInterceptor extends BaseInterceptor {

    // 注入StringRedisTemplate实例
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    // 注入ApiMessageLogServiceImpl实例
    @Autowired
    ApiMessageLogServiceImpl apiMessageLogService;

    /**
     * 在请求处理前执行的方法
     *
     * @param request  HTTP请求对象
     * @param response HTTP响应对象
     * @param handler  处理请求的处理程序
     * @return 如果继续处理请求,则返回true;否则,返回false
     * @throws Exception 如果在处理过程中发生异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 从请求中获取JSON字符串
        String reqJsonStr = IOUtils.toString(request.getInputStream(), "UTF-8");

        // 创建API消息日志实体并设置基本信息
        ApiMessageLog apiMessageLog = new ApiMessageLog();
        apiMessageLog.setApiName(request.getRequestURI());
        apiMessageLog.setMsg(reqJsonStr);
        apiMessageLog.setType("1");
        apiMessageLog.setCtime(LocalDateTime.now());

        // 尝试解析JSON字符串
        JSONObject json = null;

        try {
            json = JSONObject.parseObject(reqJsonStr);
        } catch (JSONException e) {
            // 如果JSON格式错误,返回错误信息并记录日志
            String r = "请求JSON格式错误!!!";
            response.getOutputStream().write(r.getBytes("utf-8"));
            apiMessageLog.setResult(r);
            MemDataTable.API_MESSAGE_LOGS.offer(apiMessageLog);
            return false;
        }

        // 从JSON中获取OperatorID并设置到API消息日志实体
        String operatorID = json.getString("OperatorID");
        apiMessageLog.setOperatorId(operatorID);

        // 将API消息日志的ID设置到请求属性中
        request.setAttribute("ApiMessageLogId", apiMessageLog.getId());

        // 将API消息日志存储到ThreadLocal中,以便后续处理
        apiMessageLogThreadLocal.set(apiMessageLog);
        return true;
    }
}

2 拦截器配置

这里可以选择性只记录哪些请求开头的日记。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author hua
 * @Description Web配置类,实现WebMvcConfigurer接口用于配置Web相关的功能。
 */
@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    /**
     * 配置跨域访问规则
     *
     * @return CorsConfiguration对象
     */
    private CorsConfiguration corsConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许所有来源
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");

        return corsConfiguration;
    }

    /**
     * 配置CorsFilter,用于处理跨域请求
     *
     * @return CorsFilter对象
     */
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig());
        return new CorsFilter(source);
    }

    /**
     * 配置中电联拦截器的Bean
     *
     * @return ZdlInterceptor对象
     */
    @Bean
    public HandlerInterceptor getZdlInterceptor() {
        return new ZdlInterceptor();
    }

    /**
     * 配置拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getZdlInterceptor())
                // 设置不拦截的路径
                .excludePathPatterns("/public/**", "")
                // 设置拦截的路径
                .addPathPatterns("/api/zdl/**");
    }
}

3 实现效果

最终保存在数据库效果,可以根据业务情况记录,如记录访问ip,时间等。

?

?

二 注解方式实现项目日记

1 引入需用到jar包

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

? 2 实现日记注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 日记
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnno {
    /**
     * 操作内容
     */
    String desc() default "";

    /**
     * 关键参数
     * @return
     */
    String key() default "";

}
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.LocalDateTime;

@Aspect
@Component
public class LogAspect {

    @Autowired
    private ISysLogService sysLogService;

    /**
     * 定义切点
     */
    @Pointcut("@annotation(cn.enjoyiot.backend.config.aop.LogAnno)")
    public void initLogAnno() {
    }


    @Around(value = "initLogAnno()")
    public Object saveSysLog(ProceedingJoinPoint joinPoint) {


        SysLog sysLog = new SysLog();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        //获取切入点所在的方法
        Method method = signature.getMethod();
        LogAnno operation = method.getAnnotation(LogAnno.class);

        if (operation != null) {
            String value = operation.desc();
            sysLog.setOperation(value);//保存获取的操作
        }

        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = method.getName();
        sysLog.setMethod(className + "." + methodName);
        //请求的参数
        Object[] args = joinPoint.getArgs();
        //将参数所在的数组转换成json
        String params = "";
        try {
            if (args.length > 0) {
                for (Object p : args) {
                    if(checkLogParam(p)){
                        params = params + JSONObject.toJSONString(p) + " ";
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        sysLog.setParams(params);

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        sysLog.setIp(IpUtil.getRealIP(request));
        sysLog.setUri(request.getRequestURI());
        SysUser sysUser = SessionUtil.getSysUser();
        if (sysUser != null) {
            sysLog.setUserId(sysUser.getId());
            sysLog.setUsername(sysUser.getUsername());
            sysLog.setAccount(sysUser.getAccount());
        }
        LocalDateTime begin = LocalDateTime.now();

        /*执行目标方法*/
        try {
            Object result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
            sysLog.setExceptionMessage(e.getMessage());
            return RespUtil.respErr(e.getMessage());
        } finally {
            LocalDateTime end = LocalDateTime.now();
            long sec = Duration.between(begin, end).getSeconds();
            sysLog.setCreateTime(end);
            sysLog.setTime(sec);
            sysLogService.save(sysLog);

        }


    }

    private boolean checkLogParam(Object p) {

        if(p==null){
            return false;
        }
        if(StringUtils.isEmpty(p)){
            return false;
        }
        if(p instanceof HttpServletRequest){
           return false;
        }else if(p instanceof HttpServletResponse){
            return false;
        }else if(p instanceof InputStreamSource){
            return false;
        }
        return true;
    }


}

?3 注解使用

    @PostMapping(value = "/couponList")
    @LogAnno(desc="账户列表")
    @PreAuthorize("hasPermission(filterObject,'accountList')")
    public String accountList(@RequestBody PageParam param) {
       return "test";
    }

文章来源:https://blog.csdn.net/qyhua/article/details/135145062
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。