最近项目中遇到一个自己之前没有做过的小功能,即系统日志的查询,老板有事没事查看下手底下员工今天都做什么了呢?
思路:一:调接口时把操作信息保存到日志类中,这种感觉有点小low;
二:通过AOP实现保存操作日志信息,通过自定义的注解来具体实现,哪一个方法需要记录;
二(1.)首先,项目中需要引入AOP的jar包
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二(2.):首先自定义一个注解,详细说明都有
package org.asyware.radnor.model;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义行为日志注解
* @author zl
* @date 2018/12/05
*
*/
@Target({ElementType.METHOD})//目标是方法
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented//文档生成时,该注解将被包含在javadoc中,可以去掉
public @interface LogAnnotation {
/**
*
* action()解释说明:
* 这个是我们写注解时需要传入的参数值的名称,比如上述的用的是action,
* 那么我们在注解时就应该这样注解@LogAnnotation(action=”测试”),
* 当然如果你只有一个参数需要传的,那么建议各位使用value(),这样注解时会默认传入的,
* 这样我们就可以向其他注解一样@LogAnnotation(“测试”)。
* targetType()解释说明:
* 自定义,我这边是分为create、delete、update、check
* @return
*/
String action() default "";
String targetType() default "";
String remark() default "";
}
二(3.)写个切面类(包含切入点、记录操作日志、获取方法的参数、获取客户端ip地址)
这块就把日志操作信息入库了
package org.asyware.radnor.conf;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.asyware.radnor.entity.Employee;
import org.asyware.radnor.entity.Log;
import org.asyware.radnor.model.LogAnnotation;
import org.asyware.radnor.repo.EmployeeRepo;
import org.asyware.radnor.repo.LogRepo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 切面类
* @author zl
* @date 2018/12/05
*
*/
@Aspect
@Component
public class LogAopAction {
public static final String LOG_TARGET_TYPE="targetType";
public static final String LOG_ACTION="action";
public static final String LOG_REMARK="remark";
private static final Logger logger = LoggerFactory.getLogger(LogAopAction.class);
@Autowired
private LogRepo logRepo;
@Autowired
private EmployeeRepo employeeRepo;
//切入点 @annotation(logAnnotation)需要你输入你实现的是哪个注解类。
@Pointcut("@annotation(org.asyware.radnor.model.LogAnnotation)")
public void pointCutMethod() {}
/**
* 记录操作日志
* @param joinPoint
*/
@Before("pointCutMethod()")//使用上面定义的切入点
public void doServiceBefore(JoinPoint joinPoint) {
Long start = System.currentTimeMillis();
Log log=new Log();//自己写的日志类
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//获取请求的参数
String param = getParam(joinPoint);
System.out.println("1111111111参数为"+param);
Employee employee=employeeRepo.findOne(Integer.parseInt(param));
if(employee==null) {
logger .warn("employee信息为空!");
}else {
log.setEmpId(employee.getId());
log.setOperator(employee.getName());
log.setIP(getCliectIp(request));
System.out.println("000000000000IP:"+getCliectIp(request));
}
//下面开始获取 targetType,remark,action
try {
Map<String,String> map = getLogMark(joinPoint);
log.setAction(map.get(LOG_ACTION));
log.setTargetType(map.get(LOG_TARGET_TYPE));
log.setRemark(map.get(LOG_REMARK));
logRepo.save(log);
}catch (ClassNotFoundException c){
logger.error(c.getMessage());
}catch (Exception e){
logger.error("插入日志异常",e.getMessage());
}
}
/**
* 方法参数
*
* 最后一个参数必须是enpId,否则记录日志的时候会报错
* @param joinPoint
* @return
*/
private String getParam(JoinPoint joinPoint){
StringBuilder params = new StringBuilder();
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
for ( int i = (joinPoint.getArgs().length-1); i < joinPoint.getArgs().length; i++) {
if(joinPoint.getArgs()[i].getClass() == Employee.class){
/*params.append(joinPoint.getArgs()[i].toString()).append(";");*/
params.append(joinPoint.getArgs()[i].toString());
}else{
/*params.append(joinPoint.getArgs()[i]).append(";");*/
params.append(joinPoint.getArgs()[i]);
}
}
}
return params.toString();
}
private Map<String,String> getLogMark(JoinPoint joinPoint) throws ClassNotFoundException {
Map<String,String> map = new HashMap<>();
String methodName = joinPoint.getSignature().getName();
String targetName = joinPoint.getTarget().getClass().getName();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
for (Method method : methods){
if(method.getName().equals(methodName)){
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);//注解类
map.put(LOG_TARGET_TYPE,logAnnotation.targetType());//注解类中写的值
map.put(LOG_ACTION,logAnnotation.action());
map.put(LOG_REMARK,logAnnotation.remark());
}
}
return map;
}
/**
* 获取客户端ip地址
* @param request
* @return
*/
public static String getCliectIp(HttpServletRequest request){
String ip = request.getHeader("X-Real-IP");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实IP。
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
}
二(4.)最后在controller层中,在具体的方法上注入之前定义的注解,并写入此注解的属性信息,即就是通过注解把日志操作记录入库
package org.asyware.radnor.controller;
import org.asyware.radnor.entity.Custom;
import org.asyware.radnor.model.BaseResult;
import org.asyware.radnor.model.Constants;
import org.asyware.radnor.model.LogAnnotation;
import org.asyware.radnor.service.CustomService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* 客户
* @author zl
* @date 2018/12/04
*
*/
@CrossOrigin
@Api(value="客户接口")
@RestController
@RequestMapping("/custom")
public class CustomController {
@Autowired
private CustomService customService;
/**
* 添加客户信息
* @LogAnnotation
* 在需要记录操作日志的controller层采用如上注解的方式就可以在操作时记录操作的日志了
* @param custom
* @return
*/
@ApiOperation(value="添加客户信息",notes="")
@PostMapping("/insertInfo")
@LogAnnotation(action="添加客户信息",targetType="create")
public BaseResult insertInfo(@RequestBody Custom custom,
@RequestParam("empId") Integer empId) {
try {
return customService.insertInfo(custom,empId);
} catch (Exception e) {
e.printStackTrace();
return new BaseResult(Constants.EXCEPTION_CODE,Constants.EXCEPTION_MSG);
}
}
}
最后,附上我之前参考别人的相关连接,https://blog.csdn.net/banjw_129/article/details/81316111和https://blog.csdn.net/qq_35491812/article/details/81502764