SpringBoot利用AOP机制来实现日志管理,并用线程池来实现多线程日志记录的插入
- 首先建立日志记录的实体类,和MySQL数据库对应,通过逆向工程生成实体类和mapper代码,以下就是我生成的三个实体类代码。
/*** @author 旺旺米雪饼*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WebLog {private Long webLogId;private String description;private String username;private Date startTime;/*** 请求持续时间ms*/private Integer durationTime;private String basePath;private String uri;private String url;private String method;private String ip;
}
public class WebLogExample {protected String orderByClause;protected boolean distinct;protected List oredCriteria;public WebLogExample() {oredCriteria = new ArrayList();}protected abstract static class GeneratedCriteria {protected List criteria;protected GeneratedCriteria() {super();criteria = new ArrayList();}protected void addCriterion(String condition) {if (condition == null) {throw new RuntimeException("Value for condition cannot be null");}criteria.add(new Criterion(condition));}protected void addCriterion(String condition, Object value, String property) {if (value == null) {throw new RuntimeException("Value for " + property + " cannot be null");}criteria.add(new Criterion(condition, value));}protected void addCriterion(String condition, Object value1, Object value2, String property) {if (value1 == null || value2 == null) {throw new RuntimeException("Between values for " + property + " cannot be null");}criteria.add(new Criterion(condition, value1, value2));}public Criteria andUriEqualTo(String value) {addCriterion("uri =", value, "uri");return (Criteria) this;}public Criteria andUriNotEqualTo(String value) {addCriterion("uri <>", value, "uri");return (Criteria) this;}public Criteria andUriGreaterThan(String value) {addCriterion("uri >", value, "uri");return (Criteria) this;}public Criteria andUriGreaterThanOrEqualTo(String value) {addCriterion("uri >=", value, "uri");return (Criteria) this;}public Criteria andUriLessThan(String value) {addCriterion("uri <", value, "uri");return (Criteria) this;}public Criteria andUriLessThanOrEqualTo(String value) {addCriterion("uri <=", value, "uri");return (Criteria) this;}public Criteria andUriLike(String value) {addCriterion("uri like", value, "uri");return (Criteria) this;}public Criteria andUriNotLike(String value) {addCriterion("uri not like", value, "uri");return (Criteria) this;}public Criteria andUriIn(List values) {addCriterion("uri in", values, "uri");return (Criteria) this;}public Criteria andUriNotIn(List values) {addCriterion("uri not in", values, "uri");return (Criteria) this;}public Criteria andUriBetween(String value1, String value2) {addCriterion("uri between", value1, value2, "uri");return (Criteria) this;}public Criteria andUriNotBetween(String value1, String value2) {addCriterion("uri not between", value1, value2, "uri");return (Criteria) this;}public Criteria andUrlIsNull() {addCriterion("url is null");return (Criteria) this;}public Criteria andUrlIsNotNull() {addCriterion("url is not null");return (Criteria) this;}public Criteria andUrlEqualTo(String value) {addCriterion("url =", value, "url");return (Criteria) this;}public Criteria andUrlNotEqualTo(String value) {addCriterion("url <>", value, "url");return (Criteria) this;}public Criteria andUrlGreaterThan(String value) {addCriterion("url >", value, "url");return (Criteria) this;}public Criteria andUrlGreaterThanOrEqualTo(String value) {addCriterion("url >=", value, "url");return (Criteria) this;}public Criteria andUrlLessThan(String value) {addCriterion("url <", value, "url");return (Criteria) this;}public Criteria andUrlLessThanOrEqualTo(String value) {addCriterion("url <=", value, "url");return (Criteria) this;}public Criteria andUrlLike(String value) {addCriterion("url like", value, "url");return (Criteria) this;}public Criteria andUrlNotLike(String value) {addCriterion("url not like", value, "url");return (Criteria) this;}public Criteria andUrlIn(List values) {addCriterion("url in", values, "url");return (Criteria) this;}public Criteria andUrlNotIn(List values) {addCriterion("url not in", values, "url");return (Criteria) this;}public Criteria andUrlBetween(String value1, String value2) {addCriterion("url between", value1, value2, "url");return (Criteria) this;}public Criteria andUrlNotBetween(String value1, String value2) {addCriterion("url not between", value1, value2, "url");return (Criteria) this;}public Criteria andMethodIsNull() {addCriterion("method is null");return (Criteria) this;}public Criteria andMethodIsNotNull() {addCriterion("method is not null");return (Criteria) this;}public Criteria andMethodEqualTo(String value) {addCriterion("method =", value, "method");return (Criteria) this;}public Criteria andMethodNotEqualTo(String value) {addCriterion("method <>", value, "method");return (Criteria) this;}public Criteria andMethodGreaterThan(String value) {addCriterion("method >", value, "method");return (Criteria) this;}public Criteria andMethodGreaterThanOrEqualTo(String value) {addCriterion("method >=", value, "method");return (Criteria) this;}public Criteria andMethodLessThan(String value) {addCriterion("method <", value, "method");return (Criteria) this;}public Criteria andMethodLessThanOrEqualTo(String value) {addCriterion("method <=", value, "method");return (Criteria) this;}public Criteria andMethodLike(String value) {addCriterion("method like", value, "method");return (Criteria) this;}public Criteria andMethodNotLike(String value) {addCriterion("method not like", value, "method");return (Criteria) this;}public Criteria andMethodIn(List values) {addCriterion("method in", values, "method");return (Criteria) this;}public Criteria andMethodNotIn(List values) {addCriterion("method not in", values, "method");return (Criteria) this;}public Criteria andMethodBetween(String value1, String value2) {addCriterion("method between", value1, value2, "method");return (Criteria) this;}public Criteria andMethodNotBetween(String value1, String value2) {addCriterion("method not between", value1, value2, "method");return (Criteria) this;}public Criteria andIpIsNull() {addCriterion("ip is null");return (Criteria) this;}public Criteria andIpIsNotNull() {addCriterion("ip is not null");return (Criteria) this;}public Criteria andIpEqualTo(String value) {addCriterion("ip =", value, "ip");return (Criteria) this;}public Criteria andIpNotEqualTo(String value) {addCriterion("ip <>", value, "ip");return (Criteria) this;}public Criteria andIpGreaterThan(String value) {addCriterion("ip >", value, "ip");return (Criteria) this;}public Criteria andIpGreaterThanOrEqualTo(String value) {addCriterion("ip >=", value, "ip");return (Criteria) this;}public Criteria andIpLessThan(String value) {addCriterion("ip <", value, "ip");return (Criteria) this;}public Criteria andIpLessThanOrEqualTo(String value) {addCriterion("ip <=", value, "ip");return (Criteria) this;}public Criteria andIpLike(String value) {addCriterion("ip like", value, "ip");return (Criteria) this;}public Criteria andIpNotLike(String value) {addCriterion("ip not like", value, "ip");return (Criteria) this;}public Criteria andIpIn(List values) {addCriterion("ip in", values, "ip");return (Criteria) this;}public Criteria andIpNotIn(List values) {addCriterion("ip not in", values, "ip");return (Criteria) this;}public Criteria andIpBetween(String value1, String value2) {addCriterion("ip between", value1, value2, "ip");return (Criteria) this;}public Criteria andIpNotBetween(String value1, String value2) {addCriterion("ip not between", value1, value2, "ip");return (Criteria) this;}}public static class Criteria extends GeneratedCriteria {protected Criteria() {super();}}public static class Criterion {private String condition;private Object value;private Object secondValue;private boolean noValue;private boolean singleValue;private boolean betweenValue;private boolean listValue;private String typeHandler;public String getCondition() {return condition;}public Object getValue() {return value;}public Object getSecondValue() {return secondValue;}public boolean isNoValue() {return noValue;}public boolean isSingleValue() {return singleValue;}public boolean isBetweenValue() {return betweenValue;}public boolean isListValue() {return listValue;}public String getTypeHandler() {return typeHandler;}protected Criterion(String condition) {super();this.condition = condition;this.typeHandler = null;this.noValue = true;}protected Criterion(String condition, Object value, String typeHandler) {super();this.condition = condition;this.value = value;this.typeHandler = typeHandler;if (value instanceof List>) {this.listValue = true;} else {this.singleValue = true;}}protected Criterion(String condition, Object value) {this(condition, value, null);}protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {super();this.condition = condition;this.value = value;this.secondValue = secondValue;this.typeHandler = typeHandler;this.betweenValue = true;}protected Criterion(String condition, Object value, Object secondValue) {this(condition, value, secondValue, null);}}
}
/*** @author 旺旺米雪饼*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WebLogWithBLOBs extends WebLog {private String parameter;private String result;}
2.实体类所对应的mapper.java文件:
/*** @author 旺旺米雪饼*/
public interface WebLogMapper {long countByExample(WebLogExample example);int deleteByExample(WebLogExample example);int deleteByPrimaryKey(Long webLogId);int insert(WebLogWithBLOBs record);int insertSelective(WebLogWithBLOBs record);List selectByExampleWithBLOBs(WebLogExample example);List selectByExample(WebLogExample example);WebLogWithBLOBs selectByPrimaryKey(Long webLogId);int updateByExampleSelective(@Param("record") WebLogWithBLOBs record, @Param("example") WebLogExample example);int updateByExampleWithBLOBs(@Param("record") WebLogWithBLOBs record, @Param("example") WebLogExample example);int updateByExample(@Param("record") WebLog record, @Param("example") WebLogExample example);int updateByPrimaryKeySelective(WebLogWithBLOBs record);int updateByPrimaryKeyWithBLOBs(WebLogWithBLOBs record);int updateByPrimaryKey(WebLog record);int insertBatch(@Param("webLogList") List webLogList);
}
3.所对应的WebLogMapper.xml文件为:
and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition}#{listItem} and ${criterion.condition} and ${criterion.condition} #{criterion.value} and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} and ${criterion.condition}#{listItem} web_log_id, description, username, start_time, duration_time, base_path, uri, url, method, ip parameter, result delete from web_logwhere web_log_id = #{webLogId,jdbcType=BIGINT} delete from web_log SELECT LAST_INSERT_ID() insert into web_log (description, username, start_time, duration_time, base_path, uri, url, method, ip, parameter, result)values (#{description,jdbcType=VARCHAR}, #{username,jdbcType=VARCHAR}, #{startTime,jdbcType=TIMESTAMP}, #{durationTime,jdbcType=INTEGER}, #{basePath,jdbcType=VARCHAR}, #{uri,jdbcType=VARCHAR}, #{url,jdbcType=VARCHAR}, #{method,jdbcType=VARCHAR}, #{ip,jdbcType=VARCHAR}, #{parameter,jdbcType=LONGVARCHAR}, #{result,jdbcType=LONGVARCHAR})insert into web_log (description, username, start_time,duration_time, base_path, uri,url, method, ip, parameter,result)values(#{item.description,jdbcType=VARCHAR}, #{item.username,jdbcType=VARCHAR}, #{item.startTime,jdbcType=TIMESTAMP},#{item.durationTime,jdbcType=TIMESTAMP}, #{item.basePath,jdbcType=VARCHAR}, #{item.uri,jdbcType=VARCHAR},#{item.url,jdbcType=VARCHAR}, #{item.method,jdbcType=VARCHAR}, #{item.ip,jdbcType=VARCHAR}, #{item.parameter,jdbcType=VARCHAR},#{item.result,jdbcType=VARCHAR}) SELECT LAST_INSERT_ID() insert into web_logdescription, username, start_time, duration_time, base_path, uri, url, method, ip, parameter, result, #{description,jdbcType=VARCHAR}, #{username,jdbcType=VARCHAR}, #{startTime,jdbcType=TIMESTAMP}, #{durationTime,jdbcType=INTEGER}, #{basePath,jdbcType=VARCHAR}, #{uri,jdbcType=VARCHAR}, #{url,jdbcType=VARCHAR}, #{method,jdbcType=VARCHAR}, #{ip,jdbcType=VARCHAR}, #{parameter,jdbcType=LONGVARCHAR}, #{result,jdbcType=LONGVARCHAR}, update web_logweb_log_id = #{record.webLogId,jdbcType=BIGINT}, description = #{record.description,jdbcType=VARCHAR}, username = #{record.username,jdbcType=VARCHAR}, start_time = #{record.startTime,jdbcType=TIMESTAMP}, duration_time = #{record.durationTime,jdbcType=INTEGER}, base_path = #{record.basePath,jdbcType=VARCHAR}, uri = #{record.uri,jdbcType=VARCHAR}, url = #{record.url,jdbcType=VARCHAR}, method = #{record.method,jdbcType=VARCHAR}, ip = #{record.ip,jdbcType=VARCHAR}, parameter = #{record.parameter,jdbcType=LONGVARCHAR}, result = #{record.result,jdbcType=LONGVARCHAR}, update web_logset web_log_id = #{record.webLogId,jdbcType=BIGINT},description = #{record.description,jdbcType=VARCHAR},username = #{record.username,jdbcType=VARCHAR},start_time = #{record.startTime,jdbcType=TIMESTAMP},duration_time = #{record.durationTime,jdbcType=INTEGER},base_path = #{record.basePath,jdbcType=VARCHAR},uri = #{record.uri,jdbcType=VARCHAR},url = #{record.url,jdbcType=VARCHAR},method = #{record.method,jdbcType=VARCHAR},ip = #{record.ip,jdbcType=VARCHAR},parameter = #{record.parameter,jdbcType=LONGVARCHAR},result = #{record.result,jdbcType=LONGVARCHAR} update web_logset web_log_id = #{record.webLogId,jdbcType=BIGINT},description = #{record.description,jdbcType=VARCHAR},username = #{record.username,jdbcType=VARCHAR},start_time = #{record.startTime,jdbcType=TIMESTAMP},duration_time = #{record.durationTime,jdbcType=INTEGER},base_path = #{record.basePath,jdbcType=VARCHAR},uri = #{record.uri,jdbcType=VARCHAR},url = #{record.url,jdbcType=VARCHAR},method = #{record.method,jdbcType=VARCHAR},ip = #{record.ip,jdbcType=VARCHAR} update web_logdescription = #{description,jdbcType=VARCHAR}, username = #{username,jdbcType=VARCHAR}, start_time = #{startTime,jdbcType=TIMESTAMP}, duration_time = #{durationTime,jdbcType=INTEGER}, base_path = #{basePath,jdbcType=VARCHAR}, uri = #{uri,jdbcType=VARCHAR}, url = #{url,jdbcType=VARCHAR}, method = #{method,jdbcType=VARCHAR}, ip = #{ip,jdbcType=VARCHAR}, parameter = #{parameter,jdbcType=LONGVARCHAR}, result = #{result,jdbcType=LONGVARCHAR}, where web_log_id = #{webLogId,jdbcType=BIGINT} update web_logset description = #{description,jdbcType=VARCHAR},username = #{username,jdbcType=VARCHAR},start_time = #{startTime,jdbcType=TIMESTAMP},duration_time = #{durationTime,jdbcType=INTEGER},base_path = #{basePath,jdbcType=VARCHAR},uri = #{uri,jdbcType=VARCHAR},url = #{url,jdbcType=VARCHAR},method = #{method,jdbcType=VARCHAR},ip = #{ip,jdbcType=VARCHAR},parameter = #{parameter,jdbcType=LONGVARCHAR},result = #{result,jdbcType=LONGVARCHAR}where web_log_id = #{webLogId,jdbcType=BIGINT} update web_logset description = #{description,jdbcType=VARCHAR},username = #{username,jdbcType=VARCHAR},start_time = #{startTime,jdbcType=TIMESTAMP},duration_time = #{durationTime,jdbcType=INTEGER},base_path = #{basePath,jdbcType=VARCHAR},uri = #{uri,jdbcType=VARCHAR},url = #{url,jdbcType=VARCHAR},method = #{method,jdbcType=VARCHAR},ip = #{ip,jdbcType=VARCHAR}where web_log_id = #{webLogId,jdbcType=BIGINT}
4.用aop实现日志记录,Joinpoint(连接点)为webLog方法,Pointcut为项目的controller包。
/*** @author 旺旺米雪饼*/
@Aspect
@Component
@Order(1)
@Slf4j
public class WebLogAspect {/*** 定义切点表达式,指定通知功能被应用的范围*/@Pointcut("execution(public * com.bjfu.controller.*.*(..))")public void webLog() {}@Before("webLog()")public void doBefore(JoinPoint joinPoint) throws Throwable {}/*** value切入点位置* returning 自定义的变量,标识目标方法的返回值,自定义变量名必须和通知方法的形参一样* 特点:在目标方法之后执行的,能够获取到目标方法的返回值,可以根据这个返回值做不同的处理*/@AfterReturning(value = "webLog()", returning = "ret")public void doAfterReturning(Object ret) throws Throwable {}/*** 通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为* ProceedingJoinPoint切入点可以获取切入点方法上的名字、参数、注解和对象** @param joinPoint* @return* @throws Throwable*/@Around("webLog()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();//获取当前请求对象ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();assert attributes != null;// TODO 从这里拿tokenHttpServletRequest request = attributes.getRequest();//记录请求信息WebLogWithBLOBs webLog = new WebLogWithBLOBs();Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();long endTime = System.currentTimeMillis();String urlStr = request.getRequestURL().toString();webLog.setBasePath(urlStr);webLog.setIp(IpUtil.getIpAddr(request));webLog.setMethod(request.getMethod());Object parameter = getParameter(method, joinPoint.getArgs());if (parameter != null) {webLog.setParameter(parameter.toString());}//前面是前置通知,后面是后置通知Object result = joinPoint.proceed();if (result != null) {webLog.setResult(result.toString());}webLog.setDurationTime((int) (endTime - startTime));webLog.setStartTime(new Date(startTime));webLog.setUri(request.getRequestURI());webLog.setUrl(request.getRequestURL().toString());// String token = jwtUtil.getToken(request);// if (!StringUtils.isEmpty(token)) {// webLog.setUsername(jwtUtil.getId(token));// }
// String userId = jwtUtil.getId(jwtUtil.getToken(request));
// webLog.setUsername(userId);
// log.info("{}", JSONUtil.parse(webLog));// 数据插入到队列asyncTask.insertIntoQueue(webLog);log.info("监听到数据{}", webLog);return result;}@Autowiredprivate AsyncTask asyncTask;/*** 根据方法和传入的参数获取请求参数*/private Object getParameter(Method method, Object[] args) {List
5.定义一个线程池,将插入日志操作交由其他线程去做:设置一个定时器,每将十秒将操作日志记录在一个队列中,再通过LogPersistenceServiceImpl业务类实现插入操作,线程池对队列的操作可能会并发,我用synchronized关键字修饰在方法上来保证对Queue
/*** @author 旺旺米雪饼*/
@Slf4j
@Component
public class AsyncTask {/*** 线程池对队列的操作可能并发,需要用synchronized保证队列内存可见性*/
// public final static Queue DATA_QUEUE = new LinkedBlockingQueue<>();public final static Queue DATA_QUEUE = new LinkedList<>();private final List logList = new ArrayList<>();@Autowiredprivate LogPersistenceServiceImpl logPersistenceServiceImpl;private synchronized void commitLogPool() {while (DATA_QUEUE.peek() != null) {int size = DATA_QUEUE.size();logList.clear();for (int i = 0; i < size; i++) {logList.add(DATA_QUEUE.poll());}int affectRows = logPersistenceServiceImpl.insertBatch(logList);logList.clear();log.info("共有{}条日志插入成功", affectRows);}}@Async("asyncExecutor")public synchronized void insertIntoQueue(WebLog webLog) {log.info("线程池接收到数据");DATA_QUEUE.offer(webLog);}/*** 每10秒将用户的擦欧哦日志刷新到数据库*/@Scheduled(cron = "0/10 * * * * ? ")public void timer() {log.info("用户的操作日志刷新到数据库,共有条记录{}", logList.size());//spring timer定时提交任务commitLogPool();}
}
6.同样, 线程池定义如下:定义了核心线程数,最大线程数,空闲线程存活时间,并且采用ThreadPoolTaskExecutor();方式建立线程。
/*** 启用 Spring 的异步方法执行功能** @author 旺旺米雪饼*/
@EnableAsync
@Configuration
public class ExecutorConfig {// new ThreadPoolTaskExecutor();/*** 核心线程数量,默认16*/private final static int CORE_POOL_SIZE = 16;/*** 最大线程数量,默认Integer.MAX_VALUE;*/private final static int MAX_POLL_SIZE = 32;/*** 空闲线程存活时间*/private final static int KEEP_ALIVE_SECONDS = 60;/*** 线程阻塞队列容量,默认Integer.MAX_VALUE*/private final static int QUEUE_CAPACITY = 10;/*** 是否允许核心线程超时*/private final static boolean ALLOW_CORE_THREAD_TIMEOUT = false;@Bean("asyncExecutor")public Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 配置核心线程数量executor.setCorePoolSize(CORE_POOL_SIZE);// 配置最大线程数executor.setMaxPoolSize(MAX_POLL_SIZE);// 配置队列容量executor.setQueueCapacity(QUEUE_CAPACITY);// 配置空闲线程存活时间executor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);executor.setAllowCoreThreadTimeOut(ALLOW_CORE_THREAD_TIMEOUT);// 设置拒绝策略,直接在execute方法的调用线程中运行被拒绝的任务executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());// 执行初始化executor.initialize();return executor;}
}
7.业务类实现接口:
/*** @author 旺旺米雪饼* 日志表操作业务类*/
@Service
public class LogPersistenceServiceImpl {@Resourceprivate WebLogMapper webLogMapper;public int insertBatch(List webLogList) {return webLogMapper.insertBatch(webLogList);}
}
别忘了在application中加@EnableAsync和@EnableScheduling注解。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
