SpringBoot AOP 記錄操作日志、異常日志

一、創(chuàng)建日志表,表結構如下:

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;
    /**
     * 參數(shù)
     */
    @Excel(name = "參數(shù)")
    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(){ }

    /**
     *  配置環(huán)繞通知
     * @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;

/**
 * 返回數(shù)據(jù)
 * 
 * @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, "未知異常,請聯(lián)系管理員");
    }
    
    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("{");
        // 參數(shù)值
        Object[] argValues = joinPoint.getArgs();
        // 參數(shù)名稱
        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("");// 根據(jù)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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容