项目中常常要打印日志,尤其是在做接口开发中,因为要面临着对前台数据的检查。
这里我们使用的是自定义注解+AOP的方式实现日志
注解是一种能被添加到java代码中的元数据(python中的函数装饰器),类、方法、参数、变量和包都可以用注解来修饰。用来定义一个类、属性或者一些方法,以便程序能被捕译处理。相当于一个说明文件,告诉应用程序某个被注解的类或者属性是什么,要怎么处理。对被修饰的代码本身没有直接影响。
注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中
它指明被注解的类会自动继承. 更具体地说,如果定义注解时使用了 @Inherited 标记,然后用定义的注解来标注另一个父类, 父类又有一个子类(subclass),则父类的所有属性将被继承到它的子类中
这里AOP不做过多介绍,没有基础的请先学习AOP知识
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
import java.lang.annotation.*;
/**
* 操作日志注解
* @author 尹稳健~
* @version 1.0
* @time 2022/9/5
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
//注解,目标在方法上
@Target(ElementType.METHOD)
public @interface LogOperation {
/** 直接使用value那么使用注解的时候可以不用写value= */
String value() default "";
}
import com.alibaba.fastjson2.JSON;
import com.sky.base.annotation.LogOperation;
import com.sky.base.constant.DataStatus;
import com.sky.base.security.pojo.LoginUser;
import com.sky.model.SysLogOperation;
import com.sky.service.SysLogOperationService;
import com.sky.utils.IpUtils;
import com.sky.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 操作日志,切面处理类
* @author 尹稳健~
* @version 1.0
* @time 2022/9/5
*/
@Aspect
@Component
@Slf4j
public class LogOperationAspect {
@Autowired
private SysLogOperationService sysLogOperationService;
/** 切入点 */
@Pointcut("@annotation(com.sky.base.annotation.LogOperation)")
public void logPointCut(){}
/** 环绕通知处理 */
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable{
long beginTime = System.currentTimeMillis();
try {
// 调用目标方法,不写point.proceed(),方法不会调用,将结果返回
Object result = point.proceed();
// 计算执行时长
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveLog(point,time, DataStatus.NORMAL);
return result;
} catch (Exception e) {
// 计算执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveLog(point,time, DataStatus.REMOVE);
throw e;
}
}
// 保存日志到数据库
public void saveLog(ProceedingJoinPoint joinPoint, long time, Short status) throws Exception {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 通过反射获取目标方法
Method method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(),signature.getParameterTypes());
// 获取方法上的注解
LogOperation annotation = method.getAnnotation(LogOperation.class);
// 获取操作日志对象
Class<SysLogOperation> sysLogOperationClass = SysLogOperation.class;
SysLogOperation sysLogOperation = sysLogOperationClass.newInstance();
if (!Objects.isNull(annotation)){
sysLogOperation.setOperation(annotation.value());
}
// 获取当前登录用户
LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
sysLogOperation.setUsername(loginUser.getUser().getUsername());
sysLogOperation.setRequestTime((int) time);
sysLogOperation.setStatus(status);
sysLogOperation.setCreateId(loginUser.getUser().getId());
sysLogOperation.setUpdateId(loginUser.getUser().getId());
sysLogOperation.setCreateTime(LocalDateTime.now());
sysLogOperation.setUpdateTime(LocalDateTime.now());
HttpServletRequest request = ServletUtils.getRequest();
sysLogOperation.setIp(IpUtils.getIpAddr(request));
sysLogOperation.setRequestUri(request.getRequestURI());
sysLogOperation.setRequestMethod(request.getMethod());
// 获取参数
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args[0]);
sysLogOperation.setRequestParams(params);
// 保存数据
sysLogOperationService.save(sysLogOperation);
}
}
import com.sky.base.annotation.LogOperation;
import com.sky.base.constant.Constants;
import com.sky.utils.R;
import com.sky.api.sys.param.request.LoginBody;
import com.sky.base.security.service.SysLoginService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/8/9
*/
@Api(tags = "系统管理-登录")
@RestController
public class SysLoginController {
@Autowired
private SysLoginService sysLoginService;
@LogOperation("登录")
@ApiOperation("登录接口")
@PostMapping("/login")
public R login(@RequestBody @Valid LoginBody loginBody){
// Todo 校验验证码
String token = sysLoginService.login(loginBody.getUsername(), loginBody.getPassword());
Map<String, Object> map = new HashMap<>();
map.put(Constants.TOKEN,token);
return R.ok(map,"登录成功");
}
}
该博客是为了记录博主项目中遇到的日志实现,所以记录一下,大家可以看看自定义注解和切面类的写法,可能代码中存在一定问题,希望大家可以找出!