zoukankan      html  css  js  c++  java
  • SpringBoot+AOP实现记录操作日志和异常日志,并保存到数据库

    1. 简介

      项目中对日志的收集往往是非常重要的,不仅方便开发人员快速定位问题,而且越来越多的客户需要查询用户行为日志、用户审计日志等。因此,在收集日志时,不仅要考虑功能实现,而且要考虑可靠性、稳定性和不耦合性。
      在每个操作和每个方法都加上日志处理肯定时不现实的,因此使用Spring提供的AOP原理就变得非常方便。定义好切面以及切点之后,可以非常方便的打印、收集或保存日志,不影响业务性能。

    2. 初始化数据库

      创建数据库aop,并初始化表结构:

    DROP TABLE IF EXISTS `sys_log`;
    CREATE TABLE `sys_log` (
      `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
      `log_type` varchar(50) NOT NULL COMMENT '日志类型',
      `create_user_code` varchar(64) NOT NULL COMMENT '创建用户编码',
      `create_user_name` varchar(100) NOT NULL COMMENT '创建用户名称',
      `create_date` datetime NOT NULL COMMENT '创建时间',
      `request_uri` varchar(500) DEFAULT NULL COMMENT '请求URI',
      `request_method` varchar(10) DEFAULT NULL COMMENT '请求方式',
      `request_params` text COMMENT '请求参数',
      `request_ip` varchar(20) NOT NULL COMMENT '请求IP',
      `server_address` varchar(50) NOT NULL COMMENT '请求服务器地址',
      `is_exception` char(1) DEFAULT NULL COMMENT '是否异常',
      `exception_info` text COMMENT '异常信息',
      `start_time` datetime NOT NULL COMMENT '开始时间',
      `end_time` datetime NOT NULL COMMENT '结束时间',
      `execute_time` int DEFAULT NULL COMMENT '执行时间',
      `user_agent` varchar(500) DEFAULT NULL COMMENT '用户代理',
      `device_name` varchar(100) DEFAULT NULL COMMENT '操作系统',
      `browser_name` varchar(100) DEFAULT NULL COMMENT '浏览器名称',
      PRIMARY KEY (`id`) USING BTREE,
      KEY `idx_sys_log_lt` (`log_type`) USING BTREE,
      KEY `idx_sys_log_cub` (`create_user_code`) USING BTREE,
      KEY `idx_sys_log_ie` (`is_exception`) USING BTREE,
      KEY `idx_sys_log_cd` (`create_date`) USING BTREE
    ) COMMENT='系统日志表';
    

    3. 示例代码

    • 创建项目
    • 修改pom.xml
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>com.c3stones</groupId>
    	<artifactId>spring-aop-log-demo</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>spring-aop-log-demo</name>
    	<description>Spring Aop Log Demo</description>
    
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.3.4.RELEASE</version>
    	</parent>
    
    	<dependencies>
    		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>com.baomidou</groupId>
    			<artifactId>mybatis-plus-boot-starter</artifactId>
    			<version>3.3.2</version>
    		</dependency>
    		<dependency>
    			<groupId>org.projectlombok</groupId>
    			<artifactId>lombok</artifactId>
    			<optional>true</optional>
    		</dependency>
    		<dependency>
    			<groupId>cn.hutool</groupId>
    			<artifactId>hutool-all</artifactId>
    			<version>5.5.1</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-aop</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    </project>
    
    • 创建配置文件application.yml
    server:
       port: 8080
       
    spring:
       datasource:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3306/aop?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
          username: root
          password: 123456
          
    # Mybatis-plus配置
    mybatis-plus:
       mapper-locations: classpath:mapper/*.xml
       global-config:
          db-config:
             id-type: AUTO
    #   configuration:
    #      # 打印sql
    #      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 创建公共参数类
    /**
     * 公共常量类
     * 
     * @author CL
     */
    public interface Global {
    
    	/**
    	 * 成功标识
    	 */
    	Boolean TRUE = true;
    
    	/**
    	 * 失败标识
    	 */
    	Boolean FALSE = false;
    
    	/**
    	 * 是标识
    	 */
    	String YES = "1";
    
    	/**
    	 * 否标识
    	 */
    	String NO = "0";
    
    	/**
    	 * 日志级别-INFO
    	 */
    	String LOG_INGO = "INFO";
    
    	/**
    	 * 日志级别-DEBUG
    	 */
    	String LOG_DEBUG = "DEBUG";
    
    	/**
    	 * 日志级别-ERROR
    	 */
    	String LOG_ERROR = "ERROR";
    
    }
    
    • 创建工具类
    import java.io.Serializable;
    
    import com.c3stones.constants.Global;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    import lombok.ToString;
    import lombok.experimental.Accessors;
    
    /**
     * 响应工具类
     *
     * @param <T>
     * @author CL
     */
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Accessors(chain = true)
    public class R<T> implements Serializable {
    
    	private static final long serialVersionUID = 1L;
    
    	@Getter
    	@Setter
    	private Boolean code;
    
    	@Getter
    	@Setter
    	private String msg;
    
    	@Getter
    	@Setter
    	private T data;
    
    	public static <T> R<T> ok() {
    		return restResult(null, Global.TRUE, null);
    	}
    
    	public static <T> R<T> ok(T data) {
    		return restResult(data, Global.TRUE, null);
    	}
    
    	public static <T> R<T> ok(T data, String msg) {
    		return restResult(data, Global.TRUE, msg);
    	}
    
    	public static <T> R<T> failed() {
    		return restResult(null, Global.FALSE, null);
    	}
    
    	public static <T> R<T> failed(String msg) {
    		return restResult(null, Global.FALSE, msg);
    	}
    
    	public static <T> R<T> failed(T data) {
    		return restResult(data, Global.FALSE, null);
    	}
    
    	public static <T> R<T> failed(T data, String msg) {
    		return restResult(data, Global.FALSE, msg);
    	}
    
    	private static <T> R<T> restResult(T data, Boolean code, String msg) {
    		R<T> apiResult = new R<>();
    		apiResult.setCode(code);
    		apiResult.setData(data);
    		apiResult.setMsg(msg);
    		return apiResult;
    	}
    
    }
    
    /**
     * 字节转换工具类
     * 
     * @author CL
     */
    public class ByteUtils {
    
    	private static final int UNIT = 1024;
    
    	/**
    	 * 格式化字节大小
    	 * 
    	 * @param byteSize 字节大小
    	 * @return
    	 */
    	public static String formatByteSize(long byteSize) {
    
    		if (byteSize <= -1) {
    			return String.valueOf(byteSize);
    		}
    
    		double size = 1.0 * byteSize;
    
    		String type = "B";
    		if ((int) Math.floor(size / UNIT) <= 0) { // 不足1KB
    			type = "B";
    			return format(size, type);
    		}
    
    		size = size / UNIT;
    		if ((int) Math.floor(size / UNIT) <= 0) { // 不足1MB
    			type = "KB";
    			return format(size, type);
    		}
    
    		size = size / UNIT;
    		if ((int) Math.floor(size / UNIT) <= 0) { // 不足1GB
    			type = "MB";
    			return format(size, type);
    		}
    
    		size = size / UNIT;
    		if ((int) Math.floor(size / UNIT) <= 0) { // 不足1TB
    			type = "GB";
    			return format(size, type);
    		}
    
    		size = size / UNIT;
    		if ((int) Math.floor(size / UNIT) <= 0) { // 不足1PB
    			type = "TB";
    			return format(size, type);
    		}
    
    		size = size / UNIT;
    		if ((int) Math.floor(size / UNIT) <= 0) {
    			type = "PB";
    			return format(size, type);
    		}
    		return ">PB";
    	}
    
    	/**
    	 * 格式化字节大小为指定单位
    	 * 
    	 * @param size 字节大小
    	 * @param type 单位类型
    	 * @return
    	 */
    	private static String format(double size, String type) {
    		int precision = 0;
    
    		if (size * 1000 % 10 > 0) {
    			precision = 3;
    		} else if (size * 100 % 10 > 0) {
    			precision = 2;
    		} else if (size * 10 % 10 > 0) {
    			precision = 1;
    		} else {
    			precision = 0;
    		}
    
    		String formatStr = "%." + precision + "f";
    
    		if ("KB".equals(type)) {
    			return String.format(formatStr, (size)) + "KB";
    		} else if ("MB".equals(type)) {
    			return String.format(formatStr, (size)) + "MB";
    		} else if ("GB".equals(type)) {
    			return String.format(formatStr, (size)) + "GB";
    		} else if ("TB".equals(type)) {
    			return String.format(formatStr, (size)) + "TB";
    		} else if ("PB".equals(type)) {
    			return String.format(formatStr, (size)) + "PB";
    		}
    		return String.format(formatStr, (size)) + "B";
    	}
    
    }
    
    • 创建实体类
    import java.io.Serializable;
    import java.time.LocalDateTime;
    
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    
    import lombok.Data;
    
    /**
     * 系统用户
     * 
     * @author CL
     *
     */
    @Data
    @TableName("sys_log")
    public class SysLog implements Serializable {
    	private static final long serialVersionUID = 1L;
    
    	/**
    	 * 用户ID
    	 */
    	@TableId
    	private Long id;
    
    	/**
    	 * 日志类型
    	 */
    	private String logType;
    
    	/**
    	 * 创建用户编码
    	 */
    	private String createUserCode;
    
    	/**
    	 * 创建用户名称
    	 */
    	private String createUserName;
    
    	/**
    	 * 创建时间
    	 */
    	private LocalDateTime createDate;
    
    	/**
    	 * 请求URI
    	 */
    	private String requestUri;
    
    	/**
    	 * 请求方式
    	 */
    	private String requestMethod;
    
    	/**
    	 * 请求参数
    	 */
    	private String requestParams;
    
    	/**
    	 * 请求IP
    	 */
    	private String requestIp;
    
    	/**
    	 * 请求服务器地址
    	 */
    	private String serverAddress;
    
    	/**
    	 * 是否异常
    	 */
    	private String isException;
    
    	/**
    	 * 异常信息
    	 */
    	private String exceptionInfo;
    
    	/**
    	 * 开始时间
    	 */
    	private LocalDateTime startTime;
    
    	/**
    	 * 结束时间
    	 */
    	private LocalDateTime endTime;
    
    	/**
    	 * 执行时间
    	 */
    	private Long executeTime;
    
    	/**
    	 * 用户代理
    	 */
    	private String userAgent;
    
    	/**
    	 * 操作系统
    	 */
    	private String deviceName;
    
    	/**
    	 * 浏览器名称
    	 */
    	private String browserName;
    
    }
    
    • 创建Mapper
    import org.apache.ibatis.annotations.Mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.c3stones.entity.SysLog;
    
    /**
     * 系统日志Dao
     * 
     * @author CL
     *
     */
    @Mapper
    public interface SysLogDao extends BaseMapper<SysLog> {
    
    }
    
    • 创建Service
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.c3stones.entity.SysLog;
    
    /**
     * 系统日志Service
     * 
     * @author CL
     *
     */
    public interface SysLogService extends IService<SysLog> {
    
    }
    
    • 创建Service实现
    import org.springframework.stereotype.Service;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.c3stones.dao.SysLogDao;
    import com.c3stones.entity.SysLog;
    import com.c3stones.service.SysLogService;
    
    /**
     * 系统日志Service实现
     * 
     * @author CL
     *
     */
    @Service
    public class SysLogServiceImpl extends ServiceImpl<SysLogDao, SysLog> implements SysLogService {
    
    }
    
    • 创建日志访问Controller
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.c3stones.entity.SysLog;
    import com.c3stones.service.SysLogService;
    import com.c3stones.utils.R;
    
    /**
     * 系统日志Controller
     * 
     * @author CL
     *
     */
    @RestController
    @RequestMapping(value = "/sys/log")
    public class SysLogController {
    
    	@Autowired
    	private SysLogService sysLogService;
    
    	/**
    	 * 日志分页查询
    	 * 
    	 * @param start  起始页码
    	 * @param limit  每页数量
    	 * @param sysLog 系统日志
    	 * @return
    	 */
    	@RequestMapping(value = "page")
    	public R<Page<SysLog>> page(int start, int limit, SysLog sysLog) {
    		QueryWrapper<SysLog> queryWrapper = new QueryWrapper<>(sysLog);
    		queryWrapper.orderByDesc("create_date");
    		Page<SysLog> page = sysLogService.page(new Page<>(start, limit), queryWrapper);
    		return R.ok(page);
    	}
    
    }
    
    • 创建日志切面
    import java.time.LocalDateTime;
    import java.time.temporal.ChronoUnit;
    import java.util.Map;
    import java.util.Objects;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import com.c3stones.constants.Global;
    import com.c3stones.entity.SysLog;
    import com.c3stones.service.SysLogService;
    import com.c3stones.utils.ByteUtils;
    import com.c3stones.utils.R;
    
    import cn.hutool.core.convert.Convert;
    import cn.hutool.core.util.ArrayUtil;
    import cn.hutool.core.util.StrUtil;
    import cn.hutool.core.util.URLUtil;
    import cn.hutool.extra.servlet.ServletUtil;
    import cn.hutool.http.useragent.UserAgent;
    import cn.hutool.http.useragent.UserAgentUtil;
    import lombok.extern.log4j.Log4j2;
    
    /**
     * 系统日志切面
     * 
     * @author CL
     *
     */
    @Log4j2
    @Aspect
    @Component
    public class SysLogAspect {
    
    	private ThreadLocal<SysLog> sysLogThreadLocal = new ThreadLocal<>();
    
    	@Autowired
    	private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    
    	@Autowired
    	private SysLogService sysLogService;
    
    	/**
    	 * 日志切点
    	 */
    	@Pointcut("execution(public * com.c3stones.controller.*.*(..))")
    	public void sysLogAspect() {
    	}
    
    	/**
    	 * 前置通知
    	 * 
    	 * @param joinPoint
    	 * @throws Throwable
    	 */
    	@Before(value = "sysLogAspect()")
    	public void doBefore(JoinPoint joinPoint) throws Throwable {
    		HttpServletRequest request = ((ServletRequestAttributes) Objects
    				.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    
    		SysLog sysLog = new SysLog();
    		// 创建人信息请根据实际项目获取方式获取
    		sysLog.setCreateUserCode("");
    		sysLog.setCreateUserName("");
    		sysLog.setStartTime(LocalDateTime.now());
    		sysLog.setRequestUri(URLUtil.getPath(request.getRequestURI()));
    		sysLog.setRequestParams(formatParams(request.getParameterMap()));
    		sysLog.setRequestMethod(request.getMethod());
    		sysLog.setRequestIp(ServletUtil.getClientIP(request));
    		sysLog.setServerAddress(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort());
    		String userAgentStr = request.getHeader("User-Agent");
    		sysLog.setUserAgent(userAgentStr);
    		UserAgent userAgent = UserAgentUtil.parse(userAgentStr);
    		sysLog.setDeviceName(userAgent.getOs().getName());
    		sysLog.setBrowserName(userAgent.getBrowser().getName());
    
    		sysLogThreadLocal.set(sysLog);
    
    		log.info("开始计时: {}  URI: {}  IP: {}", sysLog.getStartTime(), sysLog.getRequestUri(), sysLog.getRequestIp());
    	}
    
    	/**
    	 * 返回通知
    	 * 
    	 * @param ret
    	 */
    	@AfterReturning(pointcut = "sysLogAspect()", returning = "ret")
    	public void doAfterReturning(Object ret) {
    		SysLog sysLog = sysLogThreadLocal.get();
    		sysLog.setLogType(Global.LOG_INGO);
    		sysLog.setEndTime(LocalDateTime.now());
    		sysLog.setExecuteTime(Long.valueOf(ChronoUnit.MILLIS.between(sysLog.getStartTime(), sysLog.getEndTime())));
    		R<?> r = Convert.convert(R.class, ret);
    		if (r.getCode() == Global.TRUE) {
    			sysLog.setIsException(Global.NO);
    		} else {
    			sysLog.setIsException(Global.YES);
    			sysLog.setExceptionInfo(r.getMsg());
    		}
    		threadPoolTaskExecutor.execute(new SaveLogThread(sysLog, sysLogService));
    		sysLogThreadLocal.remove();
    
    		Runtime runtime = Runtime.getRuntime();
    		log.info("计时结束: {}  用时: {}ms  URI: {}  总内存: {}  已用内存: {}", sysLog.getEndTime(), sysLog.getExecuteTime(),
    				sysLog.getRequestUri(), ByteUtils.formatByteSize(runtime.totalMemory()),
    				ByteUtils.formatByteSize(runtime.totalMemory() - runtime.freeMemory()));
    	}
    
    	/**
    	 * 异常通知
    	 * 
    	 * @param e
    	 */
    	@AfterThrowing(pointcut = "sysLogAspect()", throwing = "e")
    	public void doAfterThrowable(Throwable e) {
    		SysLog sysLog = sysLogThreadLocal.get();
    		sysLog.setLogType(Global.LOG_ERROR);
    		sysLog.setEndTime(LocalDateTime.now());
    		sysLog.setExecuteTime(Long.valueOf(ChronoUnit.MINUTES.between(sysLog.getStartTime(), sysLog.getEndTime())));
    		sysLog.setIsException(Global.YES);
    		sysLog.setExceptionInfo(e.getMessage());
    		threadPoolTaskExecutor.execute(new SaveLogThread(sysLog, sysLogService));
    		sysLogThreadLocal.remove();
    		
    		Runtime runtime = Runtime.getRuntime();
    		log.info("计时结束: {}  用时: {}ms  URI: {}  总内存: {}  已用内存: {}", sysLog.getEndTime(), sysLog.getExecuteTime(),
    				sysLog.getRequestUri(), ByteUtils.formatByteSize(runtime.totalMemory()),
    				ByteUtils.formatByteSize(runtime.totalMemory() - runtime.freeMemory()));
    	}
    
    	/**
    	 * 格式化参数
    	 * 
    	 * @param parameterMap
    	 * @return
    	 */
    	private String formatParams(Map<String, String[]> parameterMap) {
    		if (parameterMap == null) {
    			return null;
    		}
    		StringBuilder params = new StringBuilder();
    		for (Map.Entry<String, String[]> param : (parameterMap).entrySet()) {
    			if (params.length() != 0) {
    				params.append("&");
    			}
    			params.append(param.getKey() + "=");
    			if (StrUtil.endWithIgnoreCase(param.getKey(), "password")) {
    				params.append("*");
    			} else if (param.getValue() != null) {
    				params.append(ArrayUtil.join(param.getValue(), ","));
    			}
    		}
    		return params.toString();
    	}
    
    	/**
    	 * 保存日志线程
    	 * 
    	 * @author CL
    	 *
    	 */
    	private static class SaveLogThread extends Thread {
    		private SysLog sysLog;
    		private SysLogService sysLogService;
    
    		public SaveLogThread(SysLog sysLog, SysLogService sysLogService) {
    			this.sysLog = sysLog;
    			this.sysLogService = sysLogService;
    		}
    
    		@Override
    		public void run() {
    			sysLog.setCreateDate(LocalDateTime.now());
    			sysLogService.save(sysLog);
    		}
    	}
    }
    
    • 创建示例Controller
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.c3stones.utils.R;
    
    /**
     * 示例Controller
     * 
     * @author CL
     *
     */
    @RestController
    public class DemoController {
    
    	/**
    	 * 示例方法1
    	 * 
    	 * @return
    	 */
    	@RequestMapping(value = "demo1")
    	public R<String> demo1(String str) {
    		return R.ok("成功返回 -> " + str);
    	}
    
    	/**
    	 * 示例方法2
    	 * 
    	 * @return
    	 */
    	@RequestMapping(value = "demo2")
    	public R<String> demo2(String str, int num) {
    		return R.failed("失败返回");
    	}
    
    	/**
    	 * 示例方法3
    	 * 
    	 * @return
    	 */
    	@SuppressWarnings("unused")
    	@RequestMapping(value = "demo3")
    	public R<String> demo3() {
    
    		int a = 1 / 0;
    
    		return R.ok("模拟异常");
    	}
    
    }
    
    • 创建启动类
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * 启动类
     * 
     * @author CL
     *
     */
    @SpringBootApplication
    public class Application {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Application.class);
    	}
    
    }
    

    4. 测试

    # 接口返回:
    {
    	"code": true,
    	"msg": null,
    	"data": "成功返回 -> demo1"
    }
    
    # 控制台打印
    2021-07-04 14:45:12.077  INFO 16032 --- [nio-8080-exec-4] com.c3stones.aspect.SysLogAspect         : 开始计时: 2021-07-04T14:45:12.076  URI: /demo1  IP: 127.0.0.1
    2021-07-04 14:45:12.077  INFO 16032 --- [nio-8080-exec-4] com.c3stones.aspect.SysLogAspect         : 计时结束: 2021-07-04T14:45:12.077  用时: 1ms  URI: /demo1  总内存: 323.5MB  已用内存: 70.332MB
    
    # 接口返回:
    {
    	"code": false,
    	"msg": "失败返回",
    	"data": null
    }
    
    # 控制台打印
    2021-07-04 14:48:57.795  INFO 16032 --- [nio-8080-exec-6] com.c3stones.aspect.SysLogAspect         : 开始计时: 2021-07-04T14:48:57.794  URI: /demo2  IP: 127.0.0.1
    2021-07-04 14:48:57.796  INFO 16032 --- [nio-8080-exec-6] com.c3stones.aspect.SysLogAspect         : 计时结束: 2021-07-04T14:48:57.795  用时: 1ms  URI: /demo2  总内存: 323.5MB  已用内存: 72.496MB
    
    # 接口返回:
    异常无返回
    
    # 控制台打印
    2021-07-04 14:49:30.260  INFO 16032 --- [nio-8080-exec-7] com.c3stones.aspect.SysLogAspect         : 开始计时: 2021-07-04T14:49:30.260  URI: /demo3  IP: 127.0.0.1
    2021-07-04 14:49:30.261  INFO 16032 --- [nio-8080-exec-7] com.c3stones.aspect.SysLogAspect         : 计时结束: 2021-07-04T14:49:30.261  用时: 0ms  URI: /demo3  总内存: 323.5MB  已用内存: 72.861MB
    2021-07-04 14:49:30.272 ERROR 16032 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause
    
    java.lang.ArithmeticException: / by zero
    	......
    
    • 访问日志分页接口::<a href="http://127.0.0.1:8080/sys/log/page?start=0&limit=10" target="_blank"http://127.0.0.1:8080/sys/log/page?start=0&limit=10
    # 接口返回:
    {
    	"code": true,
    	"msg": null,
    	"data": {
    		"records": [
    			{
    				"id": 3,
    				"logType": "ERROR",
    				"createUserCode": "",
    				"createUserName": "",
    				"createDate": "2021-07-04T14:49:30",
    				"requestUri": "/demo3",
    				"requestMethod": "GET",
    				"requestParams": "",
    				"requestIp": "127.0.0.1",
    				"serverAddress": "http://127.0.0.1:8080",
    				"isException": "1",
    				"exceptionInfo": "/ by zero",
    				"startTime": "2021-07-04T14:49:30",
    				"endTime": "2021-07-04T14:49:30",
    				"executeTime": 0,
    				"userAgent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
    				"deviceName": "Windows 10 or Windows Server 2016",
    				"browserName": "Chrome"
    			},
    			
    			{
    				"id": 2,
    				"logType": "INFO",
    				"createUserCode": "",
    				"createUserName": "",
    				"createDate": "2021-07-04T14:48:58",
    				"requestUri": "/demo2",
    				"requestMethod": "GET",
    				"requestParams": "str=demo2&num=10",
    				"requestIp": "127.0.0.1",
    				"serverAddress": "http://127.0.0.1:8080",
    				"isException": "1",
    				"exceptionInfo": "失败返回",
    				"startTime": "2021-07-04T14:48:58",
    				"endTime": "2021-07-04T14:48:58",
    				"executeTime": 1,
    				"userAgent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
    				"deviceName": "Windows 10 or Windows Server 2016",
    				"browserName": "Chrome"
    			},
    			{
    				"id": 1,
    				"logType": "INFO",
    				"createUserCode": "",
    				"createUserName": "",
    				"createDate": "2021-07-04T14:45:12",
    				"requestUri": "/demo1",
    				"requestMethod": "GET",
    				"requestParams": "str=demo1",
    				"requestIp": "127.0.0.1",
    				"serverAddress": "http://127.0.0.1:8080",
    				"isException": "0",
    				"exceptionInfo": null,
    				"startTime": "2021-07-04T14:45:12",
    				"endTime": "2021-07-04T14:45:12",
    				"executeTime": 1,
    				"userAgent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
    				"deviceName": "Windows 10 or Windows Server 2016",
    				"browserName": "Chrome"
    			}
    		],
    		"total": 0,
    		"size": 10,
    		"current": 1,
    		"orders": [],
    		"optimizeCountSql": true,
    		"hitCount": false,
    		"searchCount": true,
    		"pages": 0
    	}
    }
    

    5. 项目地址

      spring-aop-log-demo

  • 相关阅读:
    函数式接口(Functional Interface)
    解决maven install报错:java.lang.NoClassDefFoundError: org/codehaus/plexus/compiler/util/scan/InclusionScanException
    logstash收集系统日志配置
    logstash的安装,启动与输出
    elasticsearch安装head插件
    elasticsearch启动常见错误
    elasticsearch的window的安装和启动
    linux下搭建jenkins
    jenkins在搭建中常见的问题
    window下操作jenkins查看页面的几种方式
  • 原文地址:https://www.cnblogs.com/cao-lei/p/14973231.html
Copyright © 2011-2022 走看看