SpringBoot AOP 记录操作日志、异常日志

一、创建日志表,表结构如下:

image.png

二、添加Maven依赖

        
        
        
            cn.hutool
            hutool-all
            [4.1.12,)
        
        
        
            eu.bitwalker
            UserAgentUtils
            1.20
        
        

        
        
            cn.afterturn
            easypoi-spring-boot-starter
            3.3.0
        
        
        
            org.springframework.boot
            spring-boot-starter-aop
        

三、日志实体类

package io.agilefast.modules.oa.entity;

import cn.afterturn.easypoi.excel.annotation.Excel;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * 日志表
 */
@Data
@TableName("XT_LOG")
public class XtLogEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    @TableId
    private String id;
    /**
     * 操作模块
     */
    @Excel(name = "操作模块")
    private String operMudel;
    /**
     * 异常详情
     */
    @Excel(name = "异常详情")
    private String exceptionDetail;
    /**
     * 操作类型
     */
    @Excel(name = "操作类型")
    private String logType;
    /**
     * 操作方法
     */
    @Excel(name = "操作方法")
    private String method;
    /**
     * 参数
     */
    @Excel(name = "参数")
    private String params;
    /**
     * 请求ip
     */
    @Excel(name = "请求ip")
    private String requestIp;

    /**
     * 请求URL
     */
    @Excel(name = "请求URL")
    private String requestUrl;
    /**
     * 操作员名称
     */
    @Excel(name = "操作员名称")
    private String username;
    /**
     * 地址
     */
    private String address;
    /**
     * 浏览器
     */
    @Excel(name = "浏览器")
    private String browser;
    /**
     * 请求耗时
     */
    @Excel(name = "请求耗时")
    private Long  time;

    /**
     * 操作时间
     */
    @Excel(name = "操作时间",format = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    public XtLogEntity() {
        super();
    }

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

四、日志注解类

package io.agilefast.modules.oa.annotion;

import java.lang.annotation.*;

/**
 * 日志注解类
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    String operModul() default "";
}

五、日志切面类

package io.agilefast.modules.oa.aop;
import cn.hutool.json.JSONObject;
import io.agilefast.common.shiro.ShiroUtils;
import io.agilefast.common.utils.R;
import io.agilefast.modules.oa.entity.XtLogEntity;
import io.agilefast.modules.oa.service.XtLogService;
import io.agilefast.modules.oa.utlils.RequestHolder;
import io.agilefast.modules.oa.utlils.StringUtils;
import io.agilefast.modules.oa.utlils.ThrowableUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
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 javax.servlet.http.HttpServletRequest;

@Component
@Aspect
@Slf4j
public class LogAspect {
    @Autowired
    private XtLogService xtLogService;

    private long currentTime = 0L;

    @Pointcut("@annotation(io.agilefast.modules.oa.annotion.Log)")
    public void logPointcut(){ }

    /**
     *  配置环绕通知
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        currentTime = System.currentTimeMillis();
        result = joinPoint.proceed();
        XtLogEntity log = new XtLogEntity("INFO",System.currentTimeMillis() - currentTime);
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        xtLogService.save(getUsername(),StringUtils.getBrowser(request),StringUtils.getIp(request),request.getRequestURI(),joinPoint,log);
        // 获取增强方法返回值,并转换为原方法返回值类型
        R r = (R) result;
        return r;
    }

    /**
     *  配置异常通知
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "logPointcut()",throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint,Throwable e){
       XtLogEntity log = new XtLogEntity("ERROR",System.currentTimeMillis() - currentTime);
       log.setExceptionDetail(ThrowableUtil.stackTraceToString(e.getClass().getName(),e.getMessage(),e.getStackTrace()));
       HttpServletRequest request = RequestHolder.getHttpServletRequest();
       xtLogService.save(getUsername(),StringUtils.getBrowser(request),StringUtils.getIp(request),request.getRequestURI(), (ProceedingJoinPoint) joinPoint,log);
    }

    // 通过shiro获取登录名
    private String getUsername(){
        try{
            return ShiroUtils.getUserEntity().getUserName();
        }catch (Exception e){
            return "";
        }
    }
}

六、辅助工具类

1.获取HttpServletRequest

package io.agilefast.modules.oa.utlils;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

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

2.获取ip、浏览器

package io.agilefast.modules.oa.utlils;
import cn.hutool.json.JSONObject;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;
import io.agilefast.common.shiro.ShiroUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 *  字符串工具类
 */
public class StringUtils extends org.apache.commons.lang.StringUtils {

    /**
     *  获取浏览器名称
     * @param request
     * @return
     */
    public static String getBrowser(HttpServletRequest request){
        UserAgent userAgent =  UserAgent.parseUserAgentString(request.getHeader("User-Agent"));

        Browser browser = userAgent.getBrowser();

        return browser.getName();
    }

    /**
     *  获取ip
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request){
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getRemoteAddr();
        }
        if (ip.contains(",")){
            ip = ip.split(",")[0];
        }
        if ("127.0.0.1".equals(ip)){
            try{
                ip = InetAddress.getLocalHost().getHostAddress();
            }catch (UnknownHostException e){
                e.printStackTrace();
            }
        }
        return ip;
    }
}

3.接口返回工具类

package io.agilefast.common.utils;

import java.util.HashMap;
import java.util.Map;

/**
 * 返回数据
 * 
 * @author
 * @email
 * @date 2016年10月27日 下午9:59:27
 */
public class R extends HashMap {
    private static final long serialVersionUID = 1L;
    
    public R() {
        put("code", 0);
        put("msg", "success");
    }
    
    public static R error() {
        return error(500, "未知异常,请联系管理员");
    }
    
    public static R error(String msg) {
        return error(500, msg);
    }
    
    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }
    
    public static R ok(Map map) {
        R r = new R();
        r.putAll(map);
        return r;
    }
    
    public static R ok() {
        return new R();
    }

    @Override
    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}

4.异常工具类

package io.agilefast.modules.oa.utlils;

import javax.validation.ConstraintViolationException;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 *  异常工具
 */
public class ThrowableUtil {

    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public static String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }
}

七、日志serviceImpl,mybatis-plus作为持久层

package io.agilefast.modules.oa.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.agilefast.common.utils.PageUtils;
import io.agilefast.common.utils.Query;
import io.agilefast.modules.oa.annotion.Log;
import io.agilefast.modules.oa.dao.XtLogDao;
import io.agilefast.modules.oa.entity.XtLogEntity;
import io.agilefast.modules.oa.service.XtLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.*;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;


@Service("xtLogService")
public class XtLogServiceImpl extends ServiceImpl implements XtLogService{

    private final XtLogDao xtLogDao;

    public XtLogServiceImpl(XtLogDao xtLogDao) {
        this.xtLogDao = xtLogDao;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(String username, String browser, String ip, String url, ProceedingJoinPoint joinPoint, XtLogEntity xtLogEntity) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log aopLog =  method.getAnnotation(Log.class);
        // 方法路径
        String methodName = joinPoint.getTarget().getClass().getName()+"."+signature.getName()+"()";
        StringBuilder params = new StringBuilder("{");
        // 参数值
        Object[] argValues = joinPoint.getArgs();
        // 参数名称
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        if (argValues != null){
            for (int i = 0; i < argValues.length; i++) {
                params.append(" ").append(argNames[i]).append(": ").append(argValues[i]);
            }
        }
        // 操作模块
        if (xtLogEntity != null){
            xtLogEntity.setOperMudel(aopLog.operModul());
        }
        assert xtLogEntity != null;
        xtLogEntity.setRequestIp(ip);

        String LOGINPATH = "login";
        if (LOGINPATH.equals(signature.getName())){
            try{
                assert argValues != null;
                // hutool json
//                username = new JSONObject(argValues[0]).get("username").toString();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        xtLogEntity.setAddress("");// 根据ip获取城市地址
        xtLogEntity.setMethod(methodName);
        xtLogEntity.setUsername(username);
        xtLogEntity.setParams(params.toString() + " }");
        xtLogEntity.setBrowser(browser);
        xtLogEntity.setId(IdUtil.randomUUID());
        xtLogEntity.setCreateTime(new Date());
        xtLogEntity.setRequestUrl(url);
        this.save(xtLogEntity);
    }

    @Override
    public PageUtils getLogList(Map params) {
        IPage page = xtLogDao.getLogList(new Query(params).getPage(), params);
        return new PageUtils(page);
    }
}

八、日志restController方法添加@Log注解

package io.agilefast.modules.oa.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.agilefast.common.base.AbstractController;
import io.agilefast.common.config.SiteConfig;
import io.agilefast.common.utils.ModelAndViewFactory;
import io.agilefast.common.utils.PageUtils;
import io.agilefast.common.utils.R;
import io.agilefast.modules.oa.annotion.Log;
import io.agilefast.modules.oa.entity.XtLogEntity;
import io.agilefast.modules.oa.entity.vo.JtScheduleVO;
import io.agilefast.modules.oa.service.XtLogService;
import io.agilefast.util.ExcelUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/xtLog")
public class XtLogController extends AbstractController {

    private final SiteConfig siteConfig;
    private final XtLogService xtLogService;

    public XtLogController(SiteConfig siteConfig, XtLogService xtLogService) {
        this.siteConfig = siteConfig;
        this.xtLogService = xtLogService;
    }

    // 日志列表分页查询
    @RequestMapping("/logList/{pageNum}/{pageSize}")
    public R logList(@RequestBody Map params, @PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize){
        params.put("Page", String.valueOf(pageNum));
        params.put("limit", String.valueOf(pageSize));
        PageUtils page  = xtLogService.getLogList(params);
        return R.ok().put("page",page);
    }


    // 清空日志
    @Log(operModul="日志管理-清空日志")
    @RequestMapping("/clearLog/{logType}")
    public R logList(@PathVariable("logType") String logType){
        List logList = xtLogService.list(new QueryWrapper().eq("LOG_TYPE",logType));
        if (logList.size() != 0){
            xtLogService.removeByIds(logList.stream().map(log -> log.getId()).collect(Collectors.toList()));
        }
        return R.ok("清空成功");
    }

    // 导出日志
    @Log(operModul="日志管理-导出日志")
    @RequestMapping("/exportLog/{logType}")
    public void exportLog( @PathVariable("logType") String logType, HttpServletResponse response){
        List logList = xtLogService.list(new QueryWrapper().eq("LOG_TYPE",logType));
        String title = "";
        if ("INFO".equals(logType)){
            title = "操作日志";
        }else if("ERROR".equals(logType)){
            title = "异常日志";
        }
        try{
            ExcelUtils.exportExcel(logList, title, title, XtLogEntity.class, title, response);
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

九、操作日志、异常日志查询功能

image.png

image.png

image.png

image.png

image.png

你可能感兴趣的:(SpringBoot AOP 记录操作日志、异常日志)