一 基本思路
- 通过aop监听@Syslog注解
- 通过反射等获取请求参数等日志信息
- 发布异步事件,异步事件监听器监听到日志数据后持久化到数据库中留痕
二 代码实现
定义日志注解,获取操作行为
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface SysLog {
/**
* 日志描述
* @return
*/
String value();
/**
* spel 表达式
* @return 日志描述
*/
String expression() default "";
}
定义异步事件和异步事件监听器
public class SysExecuteLogEvent extends ApplicationEvent {
public SysExecuteLogEvent(SysExecuteLogDto sysExecuteLogDto) {
super(sysExecuteLogDto);
}
}
@RequiredArgsConstructor
public class SysExecuteLogEventListener {
private final RemoteLogService remoteLogService;
@Async
@Order
@EventListener(SysExecuteLogEvent.class)
public void saveLog(SysExecuteLogEvent event) {
SysExecuteLogDto sysExecuteLogDto = (SysExecuteLogDto) event.getSource();
remoteLogService.saveExecuteLog(sysExecuteLogDto, SecurityConstants.FROM_IN);
}
}
定义aop切面
@Aspect
@Slf4j
@RequiredArgsConstructor
public class SysLogAspect {
private final ApplicationEventPublisher publisher;
@SneakyThrows
@Around("@annotation(sysLog)")
public Object logAspect(ProceedingJoinPoint point, SysLog sysLog){
String methodName = point.getSignature().getName();
String className = point.getTarget().getClass().getName();
log.debug("[类名]:{} [方法名]:{}",className,methodName);
String value = sysLog.value();
String expression = sysLog.expression();
if (StrUtil.isNotEmpty(expression)){
// spel提取
Method method = ((MethodSignature) point.getSignature()).getMethod();
EvaluationContext evaluationContext = SysExecuteLogUtil.getEvaluationContext(method, point.getArgs());
value = SysExecuteLogUtil.getValue(expression, evaluationContext, String.class);
}
SysExecuteLogDto sysExecuteLog = SysExecuteLogUtil.getSysExecuteLog();
sysExecuteLog.setTitle(value);
Object obj = null;
// 计算程序耗时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
obj=point.proceed();
} catch (Exception e) {
e.printStackTrace();
sysExecuteLog.setException(e.getMessage());
sysExecuteLog.setType(LogTypeEnum.ERROR.getType());
}finally {
stopWatch.stop();
long totalTimeMillis = stopWatch.getTotalTimeMillis();
sysExecuteLog.setTime(totalTimeMillis);
// 推送异步保存日志事件
SysExecuteLogEvent logEvent = new SysExecuteLogEvent(sysExecuteLog);
publisher.publishEvent(logEvent);
}
return obj;
}
}
日志工具类
public enum LogTypeEnum {
ERROR("9","失败"),
NORMAL("0","正常");
/**
* 类型
*/
@Getter
private String type;
/**
* 描述
*/
@Getter
private String description;
LogTypeEnum(String type,String description){
this.type=type;
this.description=description;
}
}
@UtilityClass
public class SysExecuteLogUtil {
public SysExecuteLogDto getSysExecuteLog(){
HttpServletRequest request = WebUtils.getRequest();
SysExecuteLogDto sysExecuteLogDto = new SysExecuteLogDto();
sysExecuteLogDto.setType(LogTypeEnum.NORMAL.getType());
sysExecuteLogDto.setServiceId(getServiceId());
sysExecuteLogDto.setCreateBy(getUserName());
String clientIP = ServletUtil.getClientIP(request);
sysExecuteLogDto.setRemoteAddr(clientIP);
sysExecuteLogDto.setUserAgent(request.getHeader(Header.USER_AGENT.getValue()));
sysExecuteLogDto.setRequestUrl(URLUtil.getPath(request.getRequestURI()));
sysExecuteLogDto.setMethod(request.getMethod());
sysExecuteLogDto.setParams(HttpUtil.toParams(request.getParameterMap()));
return sysExecuteLogDto;
}
/**
* 获取服务id
* @return
*/
private String getServiceId(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof OAuth2Authentication){
String clientId = ((OAuth2Authentication) authentication).getOAuth2Request().getClientId();
return clientId;
}
return null;
};
/**
* 获取当前登录用户
* @return
*/
private String getUserName(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication==null){
return null;
}
return authentication.getName();
}
public <T> T getValue(String key, EvaluationContext evaluationContext, Class<T> clazz){
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
Expression expression = spelExpressionParser.parseExpression(key);
return expression.getValue(evaluationContext,clazz);
}
public EvaluationContext getEvaluationContext(Method method,Object[] arguments){
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(method);
EvaluationContext standardEvaluationContext = new StandardEvaluationContext();
if (ArrayUtil.isEmpty(parameterNames)){
return standardEvaluationContext;
}
for (int i = 0; i < arguments.length; i++) {
standardEvaluationContext.setVariable(parameterNames[i],arguments[i]);
}
return standardEvaluationContext;
}
}
bean配置
@Configuration
public class SysExecuteLogAutoConfiguration {
@Autowired
private RemoteLogService remoteLogService;
@Bean
public SysExecuteLogEventListener sysExecuteLogEventListener(){
return new SysExecuteLogEventListener(remoteLogService);
}
@Bean
public SysLogAspect sysLogAspect(ApplicationEventPublisher publisher){
return new SysLogAspect(publisher);
}
}
三 客户端调用