使用AOP切面,简化系统的日志开发
项目结构
日志注解
com.xiongxin.boot.log.LogInfo
package com.xiongxin.boot.log;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogInfo {
String value() ;
}
日志切面
com.xiongxin.boot.log.LogAopAspect
package com.xiongxin.boot.log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Aspect
@Component
public class LogAopAspect {
@Around("@annotation(logInfo)")
public Object funcLogProcess(ProceedingJoinPoint point, LogInfo logInfo) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
LogInfo methodLog = AnnotationUtils.findAnnotation(method, LogInfo.class);
Logger log = LoggerFactory.getLogger(point.getTarget().getClass());
List<Object> collect = Arrays.stream(point.getArgs()).map(this::toMap).collect(Collectors.toList());
if (methodLog != null) {
log.info("{}-{}-开始:args={}", logInfo.value(), method.getName(), JSON.toJSONString(collect));
}
long currentTimeMillis = System.currentTimeMillis();
Object objResp = null;
try {
objResp = point.proceed();
} catch (Exception e) {
log.error("{}-{}-异常:args={},e.msg={}", logInfo.value(), method.getName(), JSON.toJSONString(point.getArgs()), e.getMessage(), e);
throw e;
}
if (methodLog != null) {
log.info("{}-{}-完成:args={},resp={}[{}ms]", logInfo.value(), method.getName(),
JSON.toJSONString(collect, SerializerFeature.DisableCircularReferenceDetect),
JSON.toJSONString(objResp == null ? String.valueOf("void") : objResp),
System.currentTimeMillis() - currentTimeMillis);
}
return objResp;
}
private Object toMap(Object o) {
Map map = new HashMap();
map.put(o.getClass().getSimpleName(), o);
return map;
}
}
使用日志的业务方法
com.xiongxin.boot.service.UserService
package com.xiongxin.boot.service;
import com.xiongxin.boot.log.LogInfo;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@LogInfo(value = "保存")
public void save() {
}
}
观察执行日志的测试用例
com.xiongxin.boot.service.UserServiceTest
package com.xiongxin.boot.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
@Resource
UserService userService;
@Test
public void save() {
userService.save();
}
}
日志格式设置
src/test/resources/application.yml
logging:
path: log
pattern:
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger: %msg%n"
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger: %msg%n"
日志的控制台结果
2020-03-17 15:58:06 [main] INFO com.xiongxin.boot.service.UserService: 保存-save-开始:args=[]
2020-03-17 15:58:06 [main] INFO com.xiongxin.boot.service.UserService: 保存-save-完成[4ms]:resp=null
通过记录业务执行的操作日志,方便的分析和排查业务问题。