赞
踩
建好数据库即可,表不存在会自动创建,别说什么高并发会怎么怎么样了,高并发不用搜索引擎,还想用数据库记录日志就是疯了
下面第一条是异常信息,第二条是正常的数据返回。
日志切面
import com.fasterxml.jackson.databind.ObjectMapper; import com.fu.work.entity.Log; import com.fu.work.service.LogService; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 日志切面 */ @Slf4j @Aspect @Component public class LogAspect { @Resource private LogService logService; //把fu替换成你自己的包即可 @Pointcut("execution(* com.fu.*.controller.*Controller.*(..))||execution(* com.fu.*.*.controller.*Controller.*(..))||execution(* com.fu.*.*.*.controller.*Controller.*(..))") public void logOperation() { } /** * 抛出异常时执行(Around和AfterThrowing互斥,只会有一个执行) */ @AfterThrowing(pointcut = "logOperation()", throwing = "e") public void afterThrowing(Exception e){ try { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); assert attributes != null; HttpServletRequest request = attributes.getRequest(); Map<String, String[]> parametersMap = request.getParameterMap(); Map<String, Object> parameters = new HashMap<>(); if (parametersMap != null) { parametersMap.forEach((key, value) -> { parameters.put(key, value[0]); }); } ObjectMapper om = new ObjectMapper(); Log logObj = new Log(); logObj.setUserId("userId");//通过token获取 logObj.setUsername("Meta");//通过token获取 logObj.setApplicationName("应用名称"); logObj.setCode(500);//状态码 logObj.setMethod(request.getMethod());//请求方法 logObj.setRequestURI(request.getRequestURI());//请求URI logObj.setRequestData(String.valueOf(parameters));//请求数据 logObj.setMsg(r.getMsg());//返回信息 logObj.setErrorMsg(om.writeValueAsString(e));//错误信息 logObj.setCreateTime(new Date());//创建时间 //异步入库 logService.insert(logObj); } catch (Exception ex) { log.error("日志入库异常:", ex); } } /** * 环绕是最后执行的(Around和AfterThrowing互斥,只会有一个执行) */ @Around("logOperation()") public Object doAround(ProceedingJoinPoint point) throws Throwable { Object result = point.proceed();//执行切点 try { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); assert attributes != null; HttpServletRequest request = attributes.getRequest(); Map<String, String[]> parametersMap = request.getParameterMap(); Map<String, Object> parameters = new HashMap<>(); if (parametersMap != null) { parametersMap.forEach((key, value) -> { parameters.put(key, value[0]); }); } Log logObj = new Log(); logObj.setUserId("userId");//通过token获取 logObj.setUsername("Meta");//通过token获取 logObj.setApplicationName("应用名称"); logObj.setCode(200);//状态码 logObj.setMethod(request.getMethod());//请求方法 logObj.setRequestURI(request.getRequestURI());//请求URI logObj.setRequestData(String.valueOf(parameters));//请求数据 logObj.setRespondData(String.valueOf(result));//返回数据 logObj.setMsg(r.getMsg());//返回信息 logObj.setCreateTime(new Date());//创建时间 //异步入库 log.info("打印当前A线程名称:{}", Thread.currentThread().getName()); logService.insert(logObj); } catch (Exception ex) { log.error("日志入库异常:", ex); } return result; } }
异步方法配置需要在@SpringBootApplication注解上方加上@EnableAsync注解开启异步线程
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * 异步线程配置:确保执行方法和异步执行的方法不在同一个类 */ @Configuration public class AsyncConfig { @Value("${spring.task.execution.pool.core-size}") private int corePoolSize; @Value("${spring.task.execution.pool.max-size}") private int maxPoolSize; @Value("${spring.task.execution.pool.queue-capacity}") private int queueCapacity; @Value("${spring.task.execution.thread-name-prefix}") private String threadNamePrefix; @Value("${spring.task.execution.pool.keep-alive}") private int keepAliveSeconds; @Bean("logAsync") public Executor logAsync() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setThreadNamePrefix(threadNamePrefix); executor.setKeepAliveSeconds(keepAliveSeconds); /* 拒绝处理策略 CallerRunsPolicy():交由调用方线程运行,比如 main 线程。 AbortPolicy():直接抛出异常。 DiscardPolicy():直接丢弃。 DiscardOldestPolicy():丢弃队列中最老的任务。 */ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }
@Slf4j @Service public class LogService { @Resource private LogDao logDao; /** * 异步日志入库 */ @Async("logAsync") public void insert(Log logObj) { log.info("打印当前B线程名称:{}",Thread.currentThread().getName()); //获取当前年月 String tableName = getYearMonth(0); try { logDao.insert(tableName, logObj); } catch (BadSqlGrammarException e) { log.error("入库异常:", e); //如果是不存在表,则创建表 if (1146 == e.getSQLException().getErrorCode()) { //判断下个月的日志表是否存在,如果不存在则创建 logDao.crateTable(getYearMonth(0)); //再次入库 logDao.insert(tableName, logObj); } } catch (Exception e) { log.error("未知异常:", e); } } //自定义方法============================================================================================================== /** * 获取年月 * * @param addOrReduceMonth 正数表示后几个月,负数表示前几个月,默认0,表示当前月 */ public static String getYearMonth(int addOrReduceMonth) { //获取当前月份的表 Calendar cal = Calendar.getInstance(); cal.add(Calendar.MONTH, addOrReduceMonth); SimpleDateFormat dft = new SimpleDateFormat("yyyyMM"); return dft.format(cal.getTime()); } }
@Mapper
public interface LogDao{
/**
* 创建日志表
*/
int crateTable(@Param("tableName") String tableName);
/**
* 写入日志
*/
int insert(@Param("tableName") String tableName,@Param("log") Log log);
}
<resultMap type="com.fu.work.entity.Log" id="BaseResultMap"> <result property="id" column="id" jdbcType="BIGINT"/> <result property="userId" column="user_id" jdbcType="VARCHAR"/> <result property="username" column="username" jdbcType="VARCHAR"/> <result property="applicationName" column="application_name" jdbcType="VARCHAR"/> <result property="code" column="code" jdbcType="SMALLINT"/> <result property="method" column="method" jdbcType="VARCHAR"/> <result property="requestURI" column="request_uri" jdbcType="VARCHAR"/> <result property="requestData" column="request_data" jdbcType="VARCHAR"/> <result property="respondData" column="respond_data" jdbcType="VARCHAR"/> <result property="msg" column="msg" jdbcType="VARCHAR"/> <result property="errorMsg" column="error_msg" jdbcType="VARCHAR"/> <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/> </resultMap> <sql id="Base_Column_List">id, user_id, username, application_name, code, `method`, request_uri, request_data, respond_data, msg, error_msg, create_time</sql> <!-- 创建日志表 --> <update id="crateTable" parameterType="String"> CREATE TABLE IF NOT EXISTS log_${tableName} ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `user_id` char(36) DEFAULT NULL COMMENT '用户ID', `username` varchar(64) DEFAULT NULL COMMENT '用户名', `application_name` varchar(255) DEFAULT NULL COMMENT '应用名称', `code` smallint(6) DEFAULT NULL COMMENT '状态码', `method` varchar(16) DEFAULT NULL COMMENT '请求方法', `request_uri` varchar(2048) DEFAULT NULL COMMENT '请求URI', `request_data` longtext DEFAULT NULL COMMENT '请求数据', `respond_data` longtext DEFAULT NULL COMMENT '返回数据', `msg` text DEFAULT NULL COMMENT '返回信息', `error_msg` longtext DEFAULT NULL COMMENT '详细错误信息', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), KEY `create_time` (`create_time`) USING BTREE COMMENT '创建时间索引', KEY `user_id` (`user_id`) COMMENT '用户ID索引', KEY `application_name` (`application_name`) COMMENT '应用名称索引' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; </update> <!-- 新增 --> <insert id="insert"> insert into log_${tableName} <trim prefix="(" suffix=")" suffixOverrides=","> <if test="log.id != null"> id, </if> <if test="log.userId != null"> user_id, </if> <if test="log.username != null"> username, </if> <if test="log.applicationName != null"> application_name, </if> <if test="log.code != null"> code, </if> <if test="log.method != null"> `method`, </if> <if test="log.requestURI != null"> request_uri, </if> <if test="log.requestData != null"> request_data, </if> <if test="log.respondData != null"> respond_data, </if> <if test="log.msg != null"> msg, </if> <if test="log.errorMsg != null"> error_msg, </if> <if test="log.createTime != null"> create_time, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="log.id != null"> #{log.id}, </if> <if test="log.userId != null"> #{log.userId}, </if> <if test="log.username != null"> #{log.username}, </if> <if test="log.applicationName != null"> #{log.applicationName}, </if> <if test="log.code != null"> #{log.code}, </if> <if test="log.method != null"> #{log.method}, </if> <if test="log.requestURI != null"> #{log.requestURI}, </if> <if test="log.requestData != null"> #{log.requestData}, </if> <if test="log.respondData != null"> #{log.respondData}, </if> <if test="log.msg != null"> #{log.msg}, </if> <if test="log.errorMsg != null"> #{log.errorMsg}, </if> <if test="log.createTime != null"> #{log.createTime}, </if> </trim> </insert>
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; @Data @NoArgsConstructor @AllArgsConstructor public class Log { private Long id; private String userId; private String username; private String applicationName; private Integer code; private String method; private String requestURI; private String requestData; private String respondData; private String msg; private String errorMsg; private Date createTime; }
#忽略写入日志的URI spring: jackson: date-format: yyyy-MM-dd hh:mm:ss time-zone: GMT+8 task: execution: #异步线程名称 thread-name-prefix: log-async pool: core-size: 8 max-size: 16 queue-capacity: 500 keep-alive: 60 datasource: #可以换成其它数据库驱动 driver-class-name: org.mariadb.jdbc.Driver url: jdbc:mariadb://localhost:3306/db_log?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml
dependencies {//类似于maven的dependencies
implementation 'org.springframework.boot:spring-boot-starter-web'//格式为groupId:artifactId:version
compileOnly 'org.projectlombok:lombok'//仅编译时使用,不参与打包
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-aop'
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
runtimeClasspath 'org.mariadb.jdbc:mariadb-java-client'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
}
@Async异步不生效
1、标注@Async注解的方法必须是public void的(也就是公有的、无返回值的)
2、调用@Async注解的方法不建议在同一个方法内(即:A类调用B类的异步方法,AB不建议在同一个方法中,如需要在同一个类,需要手动获取代理)
import cn.hutool.extra.spring.SpringUtil; import org.springframework.stereotype.Component; @Component public class myClass { //同类方法a调用异步方法b public String a(){ System.out.println("主线程开始执行......"); //手动获取代理,调用异步方法b SpringUtil.getApplicationContext().getBean(myClass.class).a(String str); System.out.println("主线程执行结束......"); } @Async public void b(String str){ Thread.sleep(3000);//休眠3秒,判断是否会阻塞主线程执行。 System.out.println("因为我是异步的,所以与主线程无关"+str); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。