基于Spring+Aop实现的全局日志功能(选择性打印日志记录日志)
前言
这是个人在优化项目老代码的一些学习记录,也希望能给各位有需要的大佬提供一种思路,SpringBoot同理
- 首先得导入相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
这里我使用的版本是4.2.5.RELEASE的版本
下面开始上代码
2.自定义注解
/**
* 日志输出注解
* 1.标注在参数上
* @author ex_gongzhihua
*/
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoggerOut {
}
/**
* 日志处理注解标注在方法上,扫描方法的入参和出参
* @author ex_gongzhihua
*/
@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogHandler {
}
- 枚举类
/**
* 日志级别
* @author ex_gongzhihua
*/
public enum LoggerLevel {
INFO,
DEBUG,
WARN,
ERROR
}
- 切面实现
/**
* 日志切面
* @author ex_gongzhihua
*/
@Aspect
@Component
public class LogAspect {
private final Logger log = LoggerFactory.getLogger(LogAspect.class);
private static final String RETURN_ERROR_PARAM = "数据校验失败";
private static final String RETURN_ERROR_EX = "调用接口失败";
@Autowired
private ParameterUtil parameterUtil;
@Autowired
private IAiLogService aiLogService;
/**
* 通用日志代码
*/
private void commonLogCode(int aiType, String businessType, long startTime, String responseResult, String result, String userName, String businessScenario, String channelSource) {
long endTime = System.currentTimeMillis();
insertAiLog(aiType, businessType, startTime, endTime, responseResult, (null != result && result.length() >= 1800) ? result.substring(0, 1800) : result, userName, businessScenario, channelSource);
}
/**
* 匹配所有带LogHandler注解的方法
*/
@Pointcut("execution(@com.axatp.common.annotation.LogHandler * * (..))")
public void resultLog(){
}
@Around(value = "resultLog()")
public Object around(ProceedingJoinPoint point) throws Throwable{
long startTime = 0;
String responseResult= TableStatusEnum.SUCCESS.getStatusName();
MethodSignature signature=(MethodSignature) point.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
LogHandler logHandler = method.getAnnotation(LogHandler.class);
if (logHandler==null){
return point.proceed();
}
//获取请求的类名
String className=point.getTarget().getClass().getName();
Object result=null;
//获取请求的方法名
String methodName = method.getName();
try {
//方法执行前入参打印
before(point,signature,method,className,methodName);
startTime=System.currentTimeMillis();
result=point.proceed();
}catch (BussinessException e){
printLog(LoggerLevel.ERROR,RETURN_ERROR_PARAM,e.getMessage());
responseResult = TableStatusEnum.PARAMETER_ERROR.getStatusName();
}catch (Exception e) {
printLog(LoggerLevel.ERROR,RETURN_ERROR_EX,e.getMessage());
responseResult = TableStatusEnum.FAIL.getStatusName();
} finally {
after(point,signature,method,className,methodName,result,responseResult,startTime);
}
return result;
}
/**
* 方法执行前,入参打印
* @param point 切面对象
* @param signature 署名信息对象
* @param method 切入点方法
* @param className 切入点类名
* @param methodName 切入点方法名
*/
public void before(JoinPoint point, MethodSignature signature, Method method, String className, String methodName) throws BussinessException{
String userName="";
String password="";
String channelSource="";
String businessScenario="";
Object[] args = point.getArgs();
String[] parameterNames = signature.getParameterNames();
Annotation[][] annotationArr = method.getParameterAnnotations();
for (int i = 0; i < parameterNames.length; i++) {
Annotation[] annotations=annotationArr[i];
LoggerOut loggerOut=null;
for (Annotation annotation : annotations) {
if (annotation instanceof LoggerOut){
loggerOut=(LoggerOut) annotation;
break;
}
}
if (loggerOut==null){
//未携带注解的参数不做处理
continue;
}
if ("userName".equals(parameterNames[i])){
userName=(String) args[i];
}else if ("password".equals(parameterNames[i])){
password=(String) args[i];
}else if ("channelSource".equals(parameterNames[i])){
channelSource=(String) args[i];
}else if ("businessScenario".equals(parameterNames[i])){
businessScenario=(String) args[i];
}
Object arg=args[i];
if (arg==null){
printLog(LoggerLevel.INFO,"className:{}===>methodName:{}-param-log====>param:{},value:{}",
className,methodName,parameterNames[i],null);
continue;
}
printLog(LoggerLevel.INFO,"className:{}===>methodName:{}-param-log====>param:{},value:{}",
className,methodName,parameterNames[i],arg);
}
//参数校验
parameterUtil.parameter(channelSource,businessScenario,userName,password);
}
/**
*
* @param point
* @param signature
* @param method
* @param className
* @param methodName
* @param result
* @param responseResult
* @param startTime
*/
public void after(JoinPoint point,MethodSignature signature, Method method, String className, String methodName,Object result,String responseResult,long startTime){
String userName="";
String channelSource="";
String businessScenario="";
int aiType = 0;
Result results;
Object[] args = point.getArgs();
String[] parameterNames = signature.getParameterNames();
Annotation[][] annotationArr = method.getParameterAnnotations();
for (int i = 0; i < parameterNames.length; i++) {
Annotation[] annotations=annotationArr[i];
LoggerOut loggerOut=null;
for (Annotation annotation : annotations) {
if (annotation instanceof LoggerOut){
loggerOut=(LoggerOut) annotation;
break;
}
}
if (loggerOut==null){
//未携带注解的参数不做处理
continue;
}
//根据参数名称来读取相对应参数值
if (("userName").equals(parameterNames[i])){
userName=(String) args[i];
}else if ("channelSource".equals(parameterNames[i])){
channelSource=(String) args[i];
}else if ("businessScenario".equals(parameterNames[i])){
businessScenario=(String) args[i];
}else if ("aiType".equals(parameterNames[i])){
aiType=(int)args[i];
}
}
//判断执行结果类型
if (result instanceof Result){
results=(Result) result;
commonLogCode(aiType,null,startTime,responseResult,JSON.toJSON(results.getData()).toString(),userName,businessScenario,channelSource);
}else {
if (result==null){
commonLogCode(aiType,null,startTime,responseResult,null,userName,businessScenario,channelSource);
}else {
commonLogCode(aiType,null,startTime,responseResult,result.toString(),userName,businessScenario,channelSource);
}
}
}
/**
* 记录日志
*
* @param aiType 平台类型
* @param businessType 业务类型
* @param startTime 开始时间
* @param endTime 结束时间
* @param responseResult 响应信息
* @param responseParam 响应数据
* @param operateUser 用户名
* @param businessScenario 业务来源
* @param channelSource 渠道
*/
public void insertAiLog(int aiType, String businessType, long startTime, long endTime, String responseResult, String responseParam, String operateUser, String businessScenario, String channelSource) {
AiLog aiLog = new AiLog();
aiLog.setPlatformType(String.valueOf(aiType));
aiLog.setBusinessType(businessType);
aiLog.setTimeConsume(String.valueOf(endTime - startTime));
aiLog.setResponseResult(responseResult);
aiLog.setResponseParam(responseParam);
aiLog.setCreatedBy(operateUser);
aiLog.setUpdatedBy(operateUser);
aiLog.setBusinessScenario(businessScenario);
aiLog.setChannelSource(channelSource);
aiLogService.addAiLog(aiLog);
}
/**
* 根据日志级别打印不同的日志参数
*/
public void printLog(LoggerLevel loggerLevel,String formatStr,Object... format){
if (loggerLevel== LoggerLevel.INFO){
log.info(formatStr,format);
}else if (loggerLevel==LoggerLevel.DEBUG){
log.debug(formatStr,format);
}else if (loggerLevel==LoggerLevel.ERROR){
log.error(formatStr,format);
}else if (loggerLevel==LoggerLevel.WARN){
log.warn(formatStr,format);
}
}
}
以上数据校验以及个别常量只是我个人项目所需,大家参考思路即可
使用的话两个注解配合使用即可,LoggerOut标注需要打印以及记录日志的参数
代码还不够完全完善有点冗余,我是想先记录下来,大家有更好的思路欢迎指出~