Springboot接口項目如何使用AOP記錄日志
一、 背景
一直想給項目構(gòu)建一個統(tǒng)一的日志收集系統(tǒng),先邁出第一步,構(gòu)建一個日志收集類,用AOP實現(xiàn)無侵入日志收集
二、 環(huán)境
1.此隨筆內(nèi)容基于spring boot項目
2.數(shù)據(jù)庫為mysql 5.7.9版本
3.jdk 版本為1.8
三、 說明
此版采用數(shù)據(jù)庫存儲,之后考慮使用elasticsearch等工具存儲
四、 內(nèi)容
1、構(gòu)建日志采集實體類:BaseLogMessage
public class BaseLogMessage {
private String serverIP;
private String appName;
private String method;
private String type;
private String userCode;
private String uri;
private String operationName;
private String operationStatus;
private long startTime;
private Object parameter;
private Object result;
private int SpendTime;
// 此處省略get、set
}
2、構(gòu)建一個配置文件讀取類,用于讀取配置文件中的系統(tǒng)名稱:SystemPropetiesUtil
@Configuration
public class SystemPropetiesUtil {
@Value("${spring.application.name}")
private String sysName;//系統(tǒng)名稱<br> // 此處省略get、set<br>}
3、新建一個AOP類,在控制器方法上作為切點,執(zhí)行日志收集: LogAspect
@Aspect
@Component
public class LogAspect {
@Autowired
private SystemPropetiesUtil systemPropetiesUtil;
//定義切點方法
@Pointcut("execution(public * cq..campus.prevented.controller.*.*(..))")
public void controllerLog() {
}
public static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
@Around("controllerLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
//獲取當(dāng)前請求對象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//記錄請求信息
BaseLogMessage baseLogMessage = new BaseLogMessage();
//1.獲取到所有的參數(shù)值的數(shù)組
Object[] args = joinPoint.getArgs();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
//2.獲取到方法的所有參數(shù)名稱的字符串?dāng)?shù)組
String[] parameterNames = methodSignature.getParameterNames();
Object result = joinPoint.proceed();
Method method = methodSignature.getMethod();
if (method.isAnnotationPresent(ApiOperation.class)) {
ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
baseLogMessage.setOperationName(apiOperation.value());
}
long endTime = System.currentTimeMillis();
String urlStr = request.getRequestURL().toString();
baseLogMessage.setUri(urlStr);
baseLogMessage.setType("操作日志");
baseLogMessage.setServerIP(getRemoteIP(request));
baseLogMessage.setMethod(request.getMethod());
baseLogMessage.setAppName(systemPropetiesUtil.getSysName());
baseLogMessage.setResult(result);
baseLogMessage.setParameter(getParameter(method, joinPoint.getArgs()));
baseLogMessage.setSpendTime((int) (endTime - startTime));
baseLogMessage.setStartTime(endTime);
LOGGER.info("{}", JsonUtils.objectToJson(baseLogMessage));
// 數(shù)據(jù)庫存儲操作
return result;
}
/**
* 異常返回通知,用于攔截異常日志信息 連接點拋出異常后執(zhí)行
*
* @param joinPoint 切入點
* @param e 異常信息
*/
@AfterThrowing(pointcut = "controllerLog()", throwing = "e")
public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
long startTime = System.currentTimeMillis();
if(null==kafkaClient){
kafkaClient = KafkaProducerClient.getInstance(systemPropetiesUtil.getKafkaHost());
// redisClient= RedisClient.getInstance(systemPropetiesUtil.getReidsHost(), Integer.parseInt(systemPropetiesUtil.getRedisPort()), "");
}
// 獲取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 從獲取RequestAttributes中獲取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST);
String urlStr = request.getRequestURL().toString();
BaseLogMessage baseLogMessage = new BaseLogMessage();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
StringBuffer strbuff = new StringBuffer();
for (StackTraceElement stet : elements) {
strbuff.append(stet + "\n");
}
String message = exceptionName + ":" + exceptionMessage + strbuff.toString();
try {
Object[] args = joinPoint.getArgs();
String[] parameterNames = methodSignature.getParameterNames();
long endTime = System.currentTimeMillis();
baseLogMessage.setUri(urlStr);
baseLogMessage.setType("異常日志");
baseLogMessage.setServerIP(getRemoteIP(request));
baseLogMessage.setMethod(request.getMethod());
baseLogMessage.setAppName(systemPropetiesUtil.getSysName());
baseLogMessage.setResult(message);
baseLogMessage.setParameter(getParameter(method, joinPoint.getArgs()));
baseLogMessage.setSpendTime((int) (endTime - startTime));
baseLogMessage.setStartTime(endTime);
LOGGER.info("{}", JsonUtils.objectToJson(baseLogMessage));
// 數(shù)據(jù)庫存儲操作
} catch (Exception e2) {
e2.printStackTrace();
}
}
/**
* 根據(jù)方法和傳入的參數(shù)獲取請求參數(shù)
*/
private Object getParameter(Method method, Object[] args) {
List<Object> argList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
if (requestBody != null) {
argList.add(args[i]);
}
RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
if (requestParam != null) {
Map<String, Object> map = new HashMap<>();
String key = parameters[i].getName();
if (!StringUtils.isEmpty(requestParam.value())) {
key = requestParam.value();
}
map.put(key, args[i]);
argList.add(map);
}
}
if (argList.size() == 0) {
return null;
} else if (argList.size() == 1) {
return argList.get(0);
} else {
return argList;
}
}
/**
* 獲取請求ip
*/
public static String getRemoteIP(HttpServletRequest request) {
String ip =null;
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (ip != null) {
//對于通過多個代理的情況,最后IP為客戶端真實IP,多個IP按照','分割
int position = ip.indexOf(",");
if (position > 0) {
ip = ip.substring(0, position);
}
}
return ip;
}
}
五、 問題
1、如果方法正常執(zhí)行,不進入AOP類,請檢查AOP的切點是否書寫正確。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot webSocket實現(xiàn)發(fā)送廣播、點對點消息和Android接收
解決java.lang.NoClassDefFoundError錯誤的問題
Spring主配置文件(applicationContext.xml) 導(dǎo)入約束詳解

