统一接口日志处理(日志入库)
1.环境搭建
日志数据库
-- 接口日志信息表
CREATE TABLE `log_note` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT ''主键'',
`interface_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT ''接口名称'',
`interface_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ''请求地址'',
`interface_request` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ''请求内容'',
`interface_request_time` bigint(20) DEFAULT NULL COMMENT ''请求时间'',
`interface_reponse_time` bigint(20) DEFAULT NULL COMMENT ''响应时间'',
`interface_length_time` bigint(20) DEFAULT NULL COMMENT ''消耗时长(ms)'',
`interface_reponse` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ''响应内容'',
`interface_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ''服务执行地址'',
`interface_error` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ''服务异常信息'',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 30 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
日志实体类
package com.chif.goingplus.aop.log.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 日志信息实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LogNote {
private Long id;
private String InterfaceName;
private String InterfaceUrl;
private String InterfaceRequest;
private Long InterfaceRequestTime;
private Long InterfaceReponseTime;
private Long InterfaceLengthTime;
private String InterfaceReponse;
private String InterfaceIp;
private String InterfaceError;
}
日志记录业务类
package com.chif.goingplus.aop.log.service;
import com.alibaba.fastjson.JSON;
import com.chif.goingplus.aop.log.IPUtil;
import com.chif.goingplus.aop.log.mapper.LogNoteMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author liudachu
* @description: 日志记录业务类
* @date 2021/1/18
*/
@Service
@Slf4j
public class LogNoteService {
@Resource
public LogNoteMapper logNoteMapper;
/**
* 日志插入(后期可做异步处理)
*
* @param joinPoint
* @param data
* @param logNote
*/
public void logInsert(JoinPoint joinPoint, Object data, com.chif.goingplus.aop.log.pojo.LogNote logNote) {
logNote.setInterfaceUrl(getUrl(joinPoint));
logNote.setInterfaceRequest(getRequestContent(joinPoint.getArgs()));
logNote.setInterfaceReponseTime(System.currentTimeMillis());
logNote.setInterfaceReponse(data == null ? "" : JSON.toJSONString(data));
logNote.setInterfaceLengthTime(logNote.getInterfaceReponseTime() - logNote.getInterfaceRequestTime());
logNote.setInterfaceIp(IPUtil.getLocalIP());
logNoteMapper.insertLogNote(logNote);
}
/**
* 获取访问地址
*
* @param joinPoint
* @return
*/
private String getUrl(JoinPoint joinPoint) {
StringBuffer url = new StringBuffer();
Method methodSignature = ((MethodSignature) joinPoint.getSignature()).getMethod();
Object target = joinPoint.getTarget();
methodSignature.getParameters();
RequestMapping requestMapping = target.getClass().getAnnotation(RequestMapping.class);
if (requestMapping != null && requestMapping.value().length > 0) {
url.append(requestMapping.value()[0]);
}
PostMapping postMapping = methodSignature.getAnnotation(PostMapping.class);
if (postMapping != null && postMapping.value().length > 0) {
url.append(postMapping.value()[0]);
}
GetMapping getMapping = methodSignature.getAnnotation(GetMapping.class);
if (getMapping != null && getMapping.value().length > 0) {
url.append(getMapping.value()[0]);
}
RequestMapping request = methodSignature.getAnnotation(RequestMapping.class);
if (request != null && request.value().length > 0) {
url.append(request.value()[0]);
}
DeleteMapping deleteMapping = methodSignature.getAnnotation(DeleteMapping.class);
if (deleteMapping != null && deleteMapping.value().length > 0) {
url.append(deleteMapping.value()[0]);
}
PutMapping putMapping = methodSignature.getAnnotation(PutMapping.class);
if (putMapping != null && putMapping.value().length > 0) {
url.append(putMapping.value()[0]);
}
return url.toString();
}
/**
* 获取请求内容
*
* @param objects
* @return
*/
private String getRequestContent(Object[] objects) {
StringBuffer stringBuffer = new StringBuffer();
AtomicInteger i = new AtomicInteger(1);
Arrays.stream(objects).forEach(item -> {
if (!Objects.isNull(item) && objects.length > 1) {
stringBuffer.append("参数" + (i.getAndIncrement()) + ":");
}
stringBuffer.append(JSON.toJSONString(item));
});
return stringBuffer.toString();
}
}
开启AOP配置
package com.chif.goingplus.aop;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
// 开启AOP
@EnableAspectJAutoProxy
public class AOPConfig {
}
2. 自定义@Log注解
package com.chif.goingplus.aop.log;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义日志注解(用于方法上)
* @author liudachu
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogNote {
String value() default "";
}
3.定义切面
package com.chif.goingplus.aop.log;
import com.chif.goingplus.aop.log.service.LogNoteService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* 统一接口日志处理(日志入库)
* @author liudachu
*/
@Aspect
@Component
public class LogAspect {
/**
* 日志集合,用于存储不同线程下的LogNote(日志记录)
*/
private static Map<String, com.chif.goingplus.aop.log.pojo.LogNote> logNoteMap = new ConcurrentHashMap<>();
@Resource
LogNoteService logNoteService;
/**
* 定义切点,切所有的Controller中的接口方法
*/
@Pointcut("execution(public * com.chif.goingplus.controller.*.*(..))")
public void LogAspect() {
}
/**
* 在目标方法完全执行后(return后)再执行
* @param joinPoint
* @param data
*/
@AfterReturning(value = "LogAspect()", returning = "data")
public void doAfterReturning(JoinPoint joinPoint, Object data) {
logNote(joinPoint, data);
}
/**
* 包裹目标方法
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("LogAspect()")
public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable {
Method methodSignature = ((MethodSignature) joinPoint.getSignature()).getMethod();
LogNote loggerInfo = methodSignature.getAnnotation(LogNote.class);
if (loggerInfo != null) {
//记录接口执行信息
Thread.currentThread().setName(UUID.randomUUID().toString());
com.chif.goingplus.aop.log.pojo.LogNote logNote = new com.chif.goingplus.aop.log.pojo.LogNote();
logNote.setInterfaceRequestTime(System.currentTimeMillis());
logNote.setInterfaceName(loggerInfo.value());
logNoteMap.put(Thread.currentThread().getName(), logNote);
}
Object obj;
try {
obj = joinPoint.proceed();
} catch (Throwable e) {
//持久化异常日志
logNote(joinPoint, e);
e.printStackTrace();
throw e;
}
return obj;
}
/**
* 日志记录(持久化)
*
* @param joinPoint
* @param data
* @param e
*/
private void logNote(JoinPoint joinPoint, Object data, Throwable e) {
if (!logNoteMap.containsKey(Thread.currentThread().getName())) {
//当前线程无日志记录,结束执行该持久化操作
return;
}
//持久化日志到数据库,内存(map集合)中剔除该日志
com.chif.goingplus.aop.log.pojo.LogNote logNote = logNoteMap.get(Thread.currentThread().getName());
logNote.setInterfaceError(e == null ? "" : getErrorMsg(e));
logNoteMap.remove(Thread.currentThread().getName());
logNoteService.logInsert(joinPoint, data, logNote);
}
/**
* 获取异常信息打印日志
* 格式化
* @param e
* @return
*/
private String getErrorMsg(Throwable e) {
if (e == null) {
return "";
}
StackTraceElement[] trace = e.getStackTrace();
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(e);
for (int i = 0; i < 8; i++) {
stringBuffer.append("\r\n at " + trace[i]);
}
return stringBuffer.toString();
}
/**
* 持久化接口异常日志
* @param joinPoint
* @param e
*/
private void logNote(JoinPoint joinPoint, Throwable e) {
logNote(joinPoint, null, e);
}
/**
* 持久化接口运行日志
* @param joinPoint
* @param data
*/
private void logNote(JoinPoint joinPoint, Object data) {
logNote(joinPoint, data, null);
}
}