栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用AOP切面实现日志记录

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用AOP切面实现日志记录

本文参考EL-ADMIN 后台管理系统,学习相关AOP(Aspect-Oriented Programming:面向切面编程),并实现使用AOP进行日志管理。特别感谢该项目的源代码作者:elunez

文章目录
  • 一、准备阶段
  • 二、编码阶段
  • 三、使用阶段

一、准备阶段

SpringBoot中需要先引入aop依赖


    com.baomidou
    mybatis-plus
    3.4.1


    org.springframework.boot
    spring-boot-starter-aop


    org.lionsoul
    ip2region
    1.7.2


    nl.basjes.parse.useragent
    yauaa
    5.23

创建一张sys_log的日志表

CREATE TABLE `sys_log` (
  `log_id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `log_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `method` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `params` text CHARACTER SET utf8 COLLATE utf8_general_ci,
  `request_ip` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `time` bigint DEFAULT NULL,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `browser` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `exception_detail` text CHARACTER SET utf8 COLLATE utf8_general_ci,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`log_id`) USING BTREE,
  KEY `log_create_time_index` (`create_time`) USING BTREE,
  KEY `inx_log_type` (`log_type`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3537 DEFAULT CHARSET=utf8mb3 COMMENT='系统日志';
二、编码阶段

日志工具类

@Slf4j
public class LogUtils {

    private static boolean ipLocal = false;

    private static File file = null;

    private static DbConfig config;

    // IP归属地查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true";

    
    public static String getCityInfo(String ip) {
        if (ipLocal) {
            return getLocalCityInfo(ip);
        } else {
            return getHttpCityInfo(ip);
        }
    }

    
    private static String getLocalCityInfo(String ip) {
        try {
            DataBlock dataBlock = new DbSearcher(config, file.getPath())
                    .binarySearch(ip);
            String region = dataBlock.getRegion();
            String address = region.replace("0|", "");
            char symbol = '|';
            if (address.charAt(address.length() - 1) == symbol) {
                address = address.substring(0, address.length() - 1);
            }
            return address;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return "";
    }

    
    private static String getHttpCityInfo(String ip) {
        String api = String.format(IP_URL, ip);
        JSONObject object = JSONUtil.parseObj(HttpUtil.get(api));
        return object.get("addr", String.class);
    }
}

日志实体类

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_log")
public class LogDO {

    private static final long serialVersionUID = 1L;

    
    @TableId(value = "log_id", type = IdType.AUTO)
    private Long id;

    
    private String username;

    
    private String description;

    
    private String method;

    
    private String params;

    
    private String logType;

    
    private String requestIp;

    
    private String address;

    
    private String browser;

    
    private Long time;

    
    private byte[] exceptionDetail;

    
    private Timestamp createTime;

    public LogDO(String logType, Long time) {
        this.logType = logType;
        this.time = time;
    }
}

Serivce类

public interface LogService {
    
    @Async
    void save(String username, String browser, String ip, ProceedingJoinPoint joinPoint, LogDO log);
}

Service实现类

@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogMapper logMapper;

    @Override
    public void save(String username, String browser, String ip, ProceedingJoinPoint joinPoint, LogDO log) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        MyLog aopLog = method.getAnnotation(MyLog.class);
        // 方法路径
        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
        // 描述
        if (log != null) {
            log.setDescription(aopLog.value());
        }
        assert log != null;
        log.setRequestIp(ip);

        log.setAddress(LogUtils.getCityInfo(log.getRequestIp()));
        log.setMethod(methodName);
        log.setUsername(username);
        log.setParams(getParameter(method, joinPoint.getArgs()));
        log.setBrowser(browser);
        logMapper.insert(log);
    }

    
    private String getParameter(Method method, Object[] args) {
        List argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            //将RequestParam注解修饰的参数作为请求参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map map = new HashMap<>();
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.size() == 0) {
            return "";
        }
        return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : JSONUtil.toJsonStr(argList);
    }
}
 

Mapper接口(此处使用了Mybatis-plus)

@Repository
public interface LogMapper extends baseMapper {
}

创建annotation包,创建Log注解类

public @interface MyLog {
    String value() default "";
}

创建aspect包,创建LogAspect的日志切面类,并加上@Aspect注解标记为切面

@Component
@Aspect
@Slf4j
public class LogAspect {
    
    ThreadLocal currentTime = new ThreadLocal<>();

    @Autowired
    private LogService logService;
}

在LogAspect切面类中,需要补充以下方法:

@Pointcut("@annotation(com.xxx.MyLog)")
public void logPointcut(){
    // 无方法体,主要在@Pointcut中体现@Log注解类的所在位置
}


@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    Object result;
    currentTime.set(System.currentTimeMillis());
    result = joinPoint.proceed();
    LogDO logDO = new LogDO("INFO", System.currentTimeMillis() - currentTime.get());
    currentTime.remove();
    HttpServletRequest request = getHttpServletRequest();
    logService.save("用户姓名xxx", LogUtils.getBrowser(request), LogUtils.getIp(request),joinPoint, logDO);
    return result;
}


@AfterThrowing(pointcut = "logPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
    LogDO log = new LogDO("ERROR",System.currentTimeMillis() - currentTime.get());
    currentTime.remove();
    log.setExceptionDetail(getStackTrace(e).getBytes());
    HttpServletRequest request = getHttpServletRequest();
    logService.save("用户姓名xxx", LogUtils.getBrowser(request), LogUtils.getIp(request), (ProceedingJoinPoint)joinPoint, log);
}

public static HttpServletRequest getHttpServletRequest() {
    return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
}


public static String getStackTrace(Throwable throwable){
    StringWriter sw = new StringWriter();
    try (PrintWriter pw = new PrintWriter(sw)) {
        throwable.printStackTrace(pw);
        return sw.toString();
    }
}
三、使用阶段

在对应的Controller类的方法上,加上自定义的@MyLog注解,并写上该方法的简短说明。

访问/test/go,即可在sys_log表中看到日志记录的信息。

@RestController
@RequestMapping("/test")
public class TestController {
    
    @MyLog("测试方法")
    @GetMapping("/go")
    String go(){
        return "日志记录成功!";
    }
}

后续若有时间将会实现一个简单的切面日志,并开放代码。

参考资料:

  • 前后端分离的后台管理系统 特别感谢该项目的源代码作者:elunez

  • 一份热乎的 SpringBoot 前后端分离后台管理系统分析!分模块开发、RBAC权限控制

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号