最近项目中需要记录服务端访问日志,所谓的服务端访问日志其实就是Controller层的访问日志,所以在开发过程中回顾了一下AOP相关的内容,特此记录,便于日后查阅。
本文只介绍Controller层日志记录,不涉及Service层与Mapper层日志记录:
Service层日志监控:SpringBoot通过AOP实现系统日志记录(二)-Service层日志监控
Mapper层日志监控:SpringBoot通过AOP实现系统日志记录(三)-Mapper层日志监控
1、引入依赖
org.springframework.boot
spring-boot-starter-aop
2、ControllerMonitor监控注解
/**
* @Description: Service注解
* @Author: zhangzhixiang
* @CreateDate: 2018/12/09 12:34:56
* @Version 1.0
*/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ControllerMonitor {
String value() default "";
}
3、Controller层
/**
* @Description:业务接口-controller
* @Author:zhangzhixaing
* @CreateDate:2018/08/31 16:39:52
* @Version:1.0
*/
@RestController
@RequestMapping("/api/ops/clue")
public class BusinessController extends ExceptionHandlerAdvice {
@ControllerMonitor
@RequestMapping(value = "/getBusinessByCondition", method = RequestMethod.POST)
public ResponseResult getBusinessByCondition(@RequestBody BusinessBO businessBO) throws Exception {
//此处省略业务代码
}
}
4、Controller层日志拦截器
@Aspect
@Component
public class SystemLogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private SystemLogDAO systemLogDAO;
@Pointcut("execution(public * com.cy.ops.api.*.controller..*(..))")
public void systemLog() {}
@Around(value = "systemLog()")
public ResponseResult doAround(ProceedingJoinPoint joinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
UserInfoBO userInfo = WebSession.getUserInfo(request, response);
if(userInfo == null) {
return new ResponseResult().setSuccess(false).setMessage(ResultCode.RECORD_LOGIN_EXPIRE.getMessage()).setCode(ResultCode.RECORD_LOGIN_EXPIRE.getCOde());
}
//1、记录执行时间
long startTime = System.currentTimeMillis();
ResponseResult result = (ResponseResult) joinPoint.proceed(joinPoint.getArgs());
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
//2、有无日志监控注解,有则输出
String methodName = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()";
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method targetMethod = nethodSignature.getMethod();
if(targetMethod.isAnnotationPresent(ControllerMonitor.class)) {
logger.info("**********Method: {}, Start: {}, End: {}, Total: {}ms**********", methodName, dateFormat.format(startTime), dateFormat.format(endTime), totalTime);
}
//3、入系统日志表
SystemLogDO systemLogDO = new SystemLogDO();
if(joinPoint.getArgs().length > 0){
systemLogDO.setPara(JsonToBeanUtil.beanToJSON(joinPoint.getArgs()[0]));
}
systemLogDO.setClientIp(IpUtil.getClientIp(request));
systemLogDO.setMethod(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
systemLogDO.setOperator(userInfo.getUserName());
systemLogDO.setReqUri(request.getRequestURI());
systemLogDO.setReturnData(JsonToBeanUtil.beanToJSON(result));
systemLogDO.setStartTime(String.valueOf(startTime));
systemLogDO.setEndTime(String.valueOf(endTime));
systemLogDO.setTotalTime(String.valueOf(totalTime));
systemLogDO.setGmtCreateUser(userInfo.getUserName());
systemLogDO.setGmtModifiedUser(userInfo.getUserName());
systemLogDO.setIsDelete(0);
systemLogDAO.insert(systemLogDO);
return result;
}
}
5、IPUtil工具类
package com.gcloud.common;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Description: IP工具类
* @Author: zhangzhixiang
* @CreateDate: 2018/11/08 16:08:54
* @Version: 1.0
*/
public class IPUtil {
/**
* 检查IP是否合法
* @param ip
* @return
*/
public static boolean ipValid(String ip) {
String regex0 = "(2[0-4]\\d)" + "|(25[0-5])";
String regex1 = "1\\d{2}";
String regex2 = "[1-9]\\d";
String regex3 = "\\d";
String regex = "(" + regex0 + ")|(" + regex1 + ")|(" + regex2 + ")|(" + regex3 + ")";
regex = "(" + regex + ").(" + regex + ").(" + regex + ").(" + regex + ")";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(ip);
return m.matches();
}
/**
* 获取本地ip 适合windows与linux
*
* @return
*/
public static String getLocalIP() {
String localIP = "127.0.0.1";
try {
Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
while (netInterfaces.hasMoreElements()) {
NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();
InetAddress ip = ni.getInetAddresses().nextElement();
if (!ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {
localIP = ip.getHostAddress();
break;
}
}
} catch (Exception e) {
try {
localIP = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e1) {
e1.printStackTrace();
}
}
return localIP;
}
/**
* 获取客户机的ip地址
* @param request
* @return
*/
public static String getClientIp(HttpServletRequest request) {
String 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.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("http_client_ip");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
// 如果是多级代理,那么取第一个ip为客户ip
if (ip != null && ip.indexOf(",") != -1) {
ip = ip.substring(ip.lastIndexOf(",") + 1, ip.length()).trim();
}
return ip;
}
/**
* 把ip转化为整数
* @param ip
* @return
*/
public static long translateIP2Int(String ip){
String[] intArr = ip.split("\\.");
int[] ipInt = new int[intArr.length];
for (int i = 0; i
6、结果返回实体类
public class ResponseResult implements Serializable {
private static final long serialVersionUID = 6054052582421291408L;
private String message;
private Object data;
private int code;
private boolean success;
private Long total;
public ResponseResult(){}
public ResponseResult(boolean success, Object data) {
this.success = success;
this.data = data;
}
public ResponseResult(boolean success, String message, Object data) {
this.success = success;
this.message = message;
this.data = data;
}
public String getMessage() {
return message;
}
public ResponseResult setMessage(String message) {
this.message = message;
return this;
}
public Object getData() {
return data;
}
public ResponseResult setData(Object data) {
this.data = data;
return this;
}
public boolean getSuccess() {
return success;
}
public ResponseResult setSuccess(boolean success) {
this.success = success;
return this;
}
public int getCode() {
return code;
}
public ResponseResult setCode(int code) {
this.code = code;
return this;
}
public Long getTotal() {
return success;
}
public ResponseResult setTotal(Long total) {
this.total = total;
return this;
}
}
7、日志表结构设计
字段名 | 注释 | 类型 | 长度 | 是否必填 | 是否主键 |
id | 自增ID | int | 11 | 是 | 是 |
client_ip | 客户端ip | varchar | 100 | 否 | 否 |
req_uri | 请求映射路径 | varchar | 100 | 否 | 否 |
method | 方法名 | varchar | 200 | 否 | 否 |
param | 参数 | text | 0 | 否 | 否 |
operator | 操作人姓名 | varchar | 100 | 否 | 否 |
start_time | 接口请求时间 | varchar | 50 | 否 | 否 |
end_time | 接口返回时间 | varchar | 50 | 否 | 否 |
total_time | 总消耗时间 | varchar | 50 | 否 | 否 |
return_data | 接口返回数据 | text | 0 | 否 | 否 |
gmt_create | 创建时间 | datatime | 0 | 是 | 否 |
gmt_create_user | 创建人 | varchar | 50 | 是 | 否 |
gmt_modified | 修改时间 | datatime | 0 | 是 | 否 |
gmt_modified_user | 修改人 | varchar | 50 | 是 | 否 |
is_delete | 是否删除(0:否 1:是) | tinyint | 2 | 是 | 否 |
全篇文章完全纯手打,如果觉得对您有帮助,记得加关注给好评哟~~