摘要
在我们的系统中需要记录日志,包括接口、方法的调用用户信息、用时、参数等。分布式环境中通过dubbo调用rpc服务,需要提供全局traceId追踪完整调用链路。
解决方案
- 日志中心独立部署,提供rpc服务,日志统一记录统一管理,可以记录到数据库或者log文件中
- request入口添加拦截器,采用slf4j提供的MDC记录用户信息
- 自定义注解和aspect,添加环绕切面,调用日志中心的rpc服务记录日志
- 添加dubbo拦截器,使用户信息,全局traceId可以跨服务传输
- mysql数据库添加Interceptors,将mysql日志记录到ThreadLocal中
- log-chain-spring-boot-starter
实现
-
代码结构
-
日志中心
提供RPC服务记录日志,demo中采用的dubbo服务,日志记录到数据库中@Component @Service(interfaceClass = LogApi.class, timeout = 10000) public class LogApiImpl implements LogApi{ @Autowired private LogServiceImpl logService; public void save(LogDTO dto){ LogEntity entity=new LogEntity(); BeanUtils.copyProperties(dto,entity); logService.save(entity); } }
日志内容
@Data public class LogDTO implements Serializable{ private static final long serialVersionUID = 4069882290787051188L; private Integer id; private String traceId; private String appName; private String userId; private String userName; private String methodName; private String type; private String param; private String result; private String description; private Long spendTime; private Date optTime; }
-
log-chain-spring-boot-start
类名 作用 LogChainConfiguration starter配置类 LogChainProperties 配置文件类,配置appName LogRecord 注解,配置日志描述以及方法是否记录Mysql日志 LogRecordAspect 从MDC和ThreadLocal中读取信息并记录日志 LogRecordManager ThreadLocal记录日志链 MysqlLogManager ThreadLocal记录Mysql日志信息 TraceFilter dubbo过滤器 MySQLStatementInterceptor mysql拦截器 LogRecordVO 日志VO,记录方法唯一标识以及是否记录mysql MysqlLogVO Mysql日志信息 -
servlet拦截器
拦截请求,生成全局TraceId、记录用户信息至MDC中
由于各个应用获取用户信息方式不一样,所以拦截器由各个项目实现public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String userId= SysUtil.getCurUserId((HttpServletRequest)request); if(!StringUtils.isEmpty(userId)){ UserDTO user= userService.getUserById(userId); if(user!=null){ MDCUtil.setUserName(user.getUserName()); } } MDCUtil.setUserId(userId); MDCUtil.setTraceId(); chain.doFilter(request, response); }
-
自定义注解添加日志描述
@Documented @Target({ ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface LogRecord { String description() default "" ; Boolean recordMysql() default true; }
需要记录日志的方法添加注解
@LogRecord(description = "根据用户id查询用户角色列表") public List<RoleDTO> listUserRole(String userId){ UserDTO user=getUserById(userId); if(user==null){ return new ArrayList<>();