美文网首页
SpringBoot AOP 记录操作日志、异常日志

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

作者: Memory_2e2e | 来源:发表于2020-06-08 22:40 被阅读0次

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

    image.png

    二、添加Maven依赖

            <!-- aop日志开始-->
            <!--hutool工具-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>[4.1.12,)</version>
            </dependency>
            <!--获取浏览器信息工具-->
            <dependency>
                <groupId>eu.bitwalker</groupId>
                <artifactId>UserAgentUtils</artifactId>
                <version>1.20</version>
            </dependency>
            <!-- aop日志结束-->
    
            <!--excel操作-->
            <dependency>
                <groupId>cn.afterturn</groupId>
                <artifactId>easypoi-spring-boot-starter</artifactId>
                <version>3.3.0</version>
            </dependency>
            <!--aop-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    

    三、日志实体类

    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<String, Object> {
        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<String, Object> 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<XtLogDao, XtLogEntity> 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<String, Object> params) {
            IPage<XtLogEntity> page = xtLogDao.getLogList(new Query<XtLogEntity>(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<String,Object> 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<XtLogEntity> logList = xtLogService.list(new QueryWrapper<XtLogEntity>().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<XtLogEntity> logList = xtLogService.list(new QueryWrapper<XtLogEntity>().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 记录操作日志、异常日志

          本文链接:https://www.haomeiwen.com/subject/extjtktx.html