利用swagger注解,实现日志打印,通过配置文件配置属性可写入到数据库。
创建maven项目
<groupId>com.fang</groupId>
<artifactId>system-log</artifactId>
<version>1.0</version>
pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
<dependency>
<groupId>com.battcn</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
<version>2.1.5-RELEASE</version>
</dependency>
</dependencies>
实体类
@Data
@Builder
@TableName("sys_log")
public class OperLog {
private String id;
private String operModule;
private String operDesc;
private String operMethod;
private String operRequstParam;
private String operResponse;
private String operUserName;
private String operIp;
private Timestamp operTime;
private String operUri;
private String operVersion;
}
实现日志AOP类
@Slf4j
@Aspect
@Component
public class OperLogAspect {
@Value("${project.version}")
private String operVersion;
@Value("${log.database.open:false}")
private boolean log2Database;
@Autowired
private SysLogMapper sysLogMapper;
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
@Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
public void operLogPointCut() {
}
@AfterReturning(value = "operLogPointCut()", returning = "keys")
public void saveOperLog(JoinPoint joinPoint, Object keys) {
log.info("进入日志处理" + keys);
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
if (StringUtils.isEmpty(apiOperation.notes())) {
return;
}
Api api = joinPoint.getTarget().getClass().getAnnotation(Api.class);
String className = joinPoint.getTarget().getClass().getName();
Map<String, String> paramMap = convertMap(request.getParameterMap());
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = null;
if (authentication != null && authentication.isAuthenticated()) {
username = authentication.getName();
}
final OperLog operLogBuilder = OperLog.builder().id(UUID.randomUUID().toString())
.operModule(api.value())
.operDesc(apiOperation.notes())
.operMethod(className + "." + method.getName())
.operRequstParam(JSON.toJSONString(paramMap))
.operUri(request.getRequestURI())
.operTime(new Timestamp(System.currentTimeMillis()))
.operVersion(operVersion)
.operResponse(JSON.toJSONString(keys))
.operUserName(username)
.operIp(IpUtil.getIpAddress(request))
.build();
log.info(JSON.toJSONString(operLogBuilder));
if (log2Database && sysLogMapper != null) {
executorService.submit(new Runnable() {
@Override
public void run() {
sysLogMapper.insert(operLogBuilder);
}
});
}
}
private Map<String, String> convertMap(Map<String, String[]> parameterMap) {
Map<String, String> paramMap = new HashMap<String, String>();
for (String key : parameterMap.keySet()) {
paramMap.put(key, parameterMap.get(key)[0]);
}
return paramMap;
}
}
IP工具类
public class IpUtil {
/**
* 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
*
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
*
* 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
* 192.168.1.100
*
* 用户真实IP为: 192.168.1.110
* @param request
* @return
*/
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
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.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
}
}
Java配置类
@Configuration
@ComponentScan
public class JavaConfig {
}
spring.factories文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.fang.JavaConfig
日记记录Mapper类
public interface SysLogMapper extends BaseMapper<OperLog> {
}
日志表SQL
DROP TABLE IF EXISTS `sys_log`;
CREATE TABLE `sys_log` (
`id` varchar(64) NOT NULL,
`oper_module` varchar(100) DEFAULT NULL COMMENT '操作模块',
`oper_desc` varchar(100) DEFAULT NULL COMMENT '操作描述',
`oper_method` varchar(100) DEFAULT NULL COMMENT '操作方法',
`oper_requst_param` varchar(255) DEFAULT NULL COMMENT '操作参数',
`oper_response` varchar(255) DEFAULT NULL COMMENT '响应内容',
`oper_username` varchar(100) DEFAULT NULL COMMENT '操作用户',
`oper_ip` varchar(32) DEFAULT NULL COMMENT '操作IP',
`oper_time` datetime DEFAULT NULL COMMENT '操作时间',
`oper_uri` varchar(100) DEFAULT NULL COMMENT '操作路径',
`oper_version` varchar(32) DEFAULT NULL COMMENT '操作版本',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
使用方法
pom.xml添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>com.fang</groupId>
<artifactId>system-log</artifactId>
<version>1.0</version>
</dependency>
修改配置文件
spring.main.allow-bean-definition-overriding=true
project.version=@project.version@
log.database.open=true
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
- project.version: 应用版本号,对应pom文件里的版本
- log.database.open:是否记录到数据库,默认为false
启动类添加注解
@MapperScan("com.fang.mapper")
controller添加swagger注解
@Api(value = "订单管理")
@ApiOperation(value = "计算金额", notes = "计算金额")
- @Api的value属性当做操作模块
- @ApiOperation的notes数据当做操作描述,如果没有notes属性,则不会记录日志
测试
在浏览器访问接口,可以看到在控制台打印信息,数据库有新增数据。
总结
@Aspect用于实现Spring AOP,可以在被调用的方法执行前、执行后增加自定义的逻辑。
拦截方向:
Filter -> Interceptor -> ControllerAdvice -> Aspect -> Controller
所以用ControllerAdvice对结果进行处理后,Aspect是拿不到处理后的结果。