第一步:定义接收的实体类
@Data
public class InterfaceLogDTO implements Serializable {
/**
* 日志ID
*/
@Id
@Column(name = "log_id")
private String logId;
/**
* 接口名称
*/
@Column(name = "service_name")
private String serviceName;
/**
* 传输类型(RECEIVE:接收,SEND:发送)
*/
@Column(name = "type")
private String type;
/**
* 状态(0:成功,!0:失败)
*/
@Column(name = "status")
private String status;
/**
* 返回状态信息
*/
@Column(name = "message")
private String message;
/**
* 操作用户
*/
@Column(name = "user_code")
private String userCode;
/**
* 完成时间
*/
@Column(name = "finish_date")
private Date finishDate;
/**
* 目标系统
*/
@Column(name = "target_sys")
private String targetSys;
/**
* 请求地址
*/
@Column(name = "url")
private String url;
/**
* 传输内容
*/
@Column(name = "service_info")
private String serviceInfo;
/**
* 返回信息
*/
@Column(name = "return_info")
private String returnInfo;
/**
* 报错信息
*/
@Column(name = "error_info")
private String errorInfo;
/**
* 创建时间开始
*/
@Column(name = "creationDateBegin")
private Date creationDateBegin;
/**
* 创建时间结束
*/
@Column(name = "creationDateEnd")
private Date creationDateEnd;
/**
* 操作的页面名称
*/
@Column(name = "operate_page_name")
private String operatePageName;
/**
* 访问操作页面的路径
*/
@Column(name = "operate_page_path")
private String operatePagePath;
}
第二步:定义所需要自己设置的字段(以下只是根据我的业务所设置的)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiLog {
/**
* 传输类型(RECEIVE:接收,SEND:发送)
*/
String type() default "RECEIVE";
/**
* 目标系统
*/
String targetSys() default "";
String throwing() default "";
第三步:获取日志记录所需字段的代码
@Aspect
@Slf4j
@Component
public class ApiLogAop {
@Autowired
ApiLogMapper apiLogMapper;
@Value("${insertReturnInfo.enable}")
private boolean insertReturnInfo;
String logId = null;
/**
* 环绕通知
* 1.在目标接口调用的过程中,记录其接口名称,传输类型(手动指定,默认receive),状态,返回状态信息,操作用户,目标系统(需手动指定),
* 访问地址,传输内容,返回信息,报错信息,创建时间开始,创建时间结束。
* @param pjp
* @return
* @throws Throwable
*/
@Around(value = "@annotation(cn.com.goldwind.ercp.ltc.service.sysConfig.ApiLog)")
public Object apiLog(ProceedingJoinPoint pjp) throws Throwable {
Object obj = null;
//自定义的接收entiry
InterfaceLogDTO interfaceLogDTO = new InterfaceLogDTO();
// 设置请求参数(抽取方法定义在了下面)
setServiceInfo(pjp, interfaceLogDTO);
//创建开始时间
interfaceLogDTO.setCreationDateBegin(new Date());
// 获取注解信息
MethodSignature signature = (MethodSignature) pjp.getSignature();
ApiLog annotation = signature.getMethod().getAnnotation(ApiLog.class);
if (null != annotation) {
// 设置日志基础信息
interfaceLogDTO.setLogId(UUIDKit.getUUID());//主键id
interfaceLogDTO.setServiceName(pjp.getSignature().getName());//接口名称
interfaceLogDTO.setType(annotation.type());//传输类型
interfaceLogDTO.setUserCode(request.getRemoteUser());//操作用户
interfaceLogDTO.setTargetSys(annotation.targetSys());//目标系统
interfaceLogDTO.setUrl(request.getRequestURL().toString());//访问地址
//获取request对象
ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//获取session中的内容
String pageRouter = request.getHeader("pageRouter");
String pageRouterCH = request.getHeader("pageRouterCH");
String realIp = getRealIp(request);
//解码---统一编码后进行相关字段的存储
if(null!=pageRouter && !pageRouter.isEmpty() &&
null!=pageRouterCH && !pageRouterCH.isEmpty()){
//操作页面名称
interfaceLogDTO.setOperatePageName
(java.net.URLDecoder.decode(pageRouterCH,"UTF-8"));
//访问的操作页面路径
interfaceLogDTO.setOperatePagePath
(realIp+java.net.URLDecoder.decode(pageRouter,"UTF-8"));
}
logId = interfaceLogDTO.getLogId();
}
try {
// 具体业务方法
obj = pjp.proceed();
// 记录返回参数(抽取方法定义在了下面)
setReturnInfo(obj, interfaceLogDTO,annotation.type());
// 执行获取返回状态方法(抽取方法定义在了下面)
executeCallback(obj, interfaceLogDTO);
} catch (Exception e) {
String errorMsg = e.getMessage();
interfaceLogDTO.setErrorInfo(errorMsg);
interfaceLogDTO.setStatus("1");
interfaceLogDTO.setMessage(CommonConstants.ERROR_MESSAGE);
throw new RuntimeException(e);
} finally {
try {
interfaceLogDTO.setCreationDateEnd(new Date());
// 保存日志操作
apiLogMapper.createInterfaceLog(interfaceLogDTO);
} catch (Exception e) {
String errorMsg = e.getMessage();
log.error(interfaceLogDTO.getServiceName() + "保存日志报错{}" + errorMsg);
}
}
return obj;
}
public void setReturnInfo(Object obj, InterfaceLogDTO interfaceLogDTO,String type) {
if (!ObjectUtils.isEmpty(obj)) {
try {
// 获取返回参数
String paramJson = JSON.toJSONString(obj);
JSONObject stakeholdersJson = JSONObject.parseObject(paramJson);
//配置文件中设置开关,并且根据是否是查询操作以及响应是否成功,来判断是否需要存入响应数据信息
if (!(CommonConstants.SUCCESS_MESSAGE.equals(stakeholdersJson.getString("message")) || "success".equals(stakeholdersJson.getString("message")))
&& insertReturnInfo && !"select".equals(type)
) {
interfaceLogDTO.setReturnInfo(paramJson);
}
//第二个思路简单粗暴直接使用配置文件中的开关判断是否需要存入响应数据
/*if (insertReturnInfo) {
interfaceLogDTO.setReturnInfo(paramJson);
}*/
} catch (Exception e) {
String errorMsg = e.getMessage();
interfaceLogDTO.setServiceInfo("请求参数解析报错{}" + errorMsg);
}
}
}
// 执行获取返回状态方法
public void executeCallback(Object obj, InterfaceLogDTO interfaceLogDTO) {
try {
String paramJson = JSON.toJSONString(obj);
JSONObject stakeholdersJson = JSONObject.parseObject(paramJson);
interfaceLogDTO.setStatus(stakeholdersJson.getString("code"));
interfaceLogDTO.setMessage(stakeholdersJson.getString("message"));
interfaceLogDTO.setErrorInfo(stakeholdersJson.getString("message"));
if (CommonConstants.SUCCESS_MESSAGE.equals(stakeholdersJson.getString("message")) || "success".equals(stakeholdersJson.getString("message"))) {
interfaceLogDTO.setFinishDate(new Date());
}
} catch (Exception e) {
String errorMsg = e.getMessage();
interfaceLogDTO.setErrorInfo("返回状态解析报错=>" + errorMsg);
interfaceLogDTO.setStatus("1");
interfaceLogDTO.setMessage(CommonConstants.ERROR_MESSAGE);
}
}
public void setServiceInfo(ProceedingJoinPoint pjp, InterfaceLogDTO interfaceLogDTO) {
try {
// 获取请求参数
Object[] args = pjp.getArgs();
if (!ObjectUtils.isEmpty(args)) {
String paramJson = JSON.toJSONString(args);
interfaceLogDTO.setServiceInfo(paramJson);
}
} catch (Exception e) {
String errorMsg = e.getMessage();
interfaceLogDTO.setServiceInfo("请求参数解析报错{}" + errorMsg);
}
}
/**
* 异常通知,记录未被捕获的异常
* @param joinPoint
* @param throwing
*/
@AfterThrowing(value = "@annotation(cn.com.goldwind.ercp.ltc.service.sysConfig.ApiLog)", throwing = "throwing")
public void error(JoinPoint joinPoint, Throwable throwing) {
String errlogContent = "";
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
//将报错信息写入error字段
throwing.printStackTrace(new PrintStream(byteArrayOutputStream));
errlogContent = byteArrayOutputStream.toString();
} catch (IOException e) {
e.printStackTrace();
}
apiLogMapper.updateApiLog(logId, errlogContent);
}
}
第四步:把需要用到日志的接口注入进去就ok(举例)
@ApiLog( type="insert",targetSys = "test")//定义的日志记录
public ReturnEntity inputCrmAll() //要记录的接口