本章结束后对应的节选代码文件:
Gangbb-Vue-05-Log
项目地址:https://github.com/Gang-bb/Gangbb-Vue历史遗留TODO:
- 第三章
- mybatis缓存暂时没用到,后面整合redis后用redis做缓存(整合Redis完成)。
- 重写mybatis配置文件扫描mapper(日志整合章节完成)
- 整合暂时没有用到druid数据源,后续会用到
本章将留下TODO:
- 第四章
- 登录日志还未实现。(到登录和权限模块完成)
LogAspect
从缓存获取当前的用户信息使用模拟的数据(到登录和权限模块完成)本章将解决TODO:
- 第三章
- 重写mybatis配置文件扫描mapper(日志整合章节完成)
本章只是简单的对日志工具进行引入和配置
整体思路:SLF4j+log4j2+Aop(若依的日志实现使用的是logback)
具体详细选型介绍可以查看我的文章:SpringBoot系列-- SpringBoot整合SLF4j+log4j2+aop记录web请求
原来文章的切点是设置在监听controller目录下,即所有请求。按照若依的做法是创建一个自定义@Log
注解,切点设为该注解,如此设计更加灵活和扩展,决定采用它的方案!
1. 添加依赖
1.1 Gangbb-Vue的pom.xml
<properties>
<fastjson.version>1.2.74</fastjson.version>
</properties>
<!--统一项目整体依赖版本-->
<dependencyManagement>
<dependencies>
<!-- 阿里JSON解析器 begin-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- 阿里JSON解析器 end-->
</dependencies>
</dependencyManagement>
1.2 Gangbb-core的pom.xml
<dependencies>
<!-- SpringBoot Web容器 begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--以下是排除的组件-->
<exclusions>
<!--排除默认自带的log组件-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- SpringBoot Web容器 end-->
<!-- SpringBoot 拦截器 begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- SpringBoot 拦截器 end-->
<!--log4j2日志相关 begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--log4j2日志相关 end-->
<!--项目common通用工具类 begin-->
<dependency>
<groupId>com.gangbb</groupId>
<artifactId>Gangbb-common</artifactId>
</dependency>
<!--项目common通用工具类 end-->
</dependencies>
1.2 Gangbb-common的pom.xml
<dependencies>
<!-- SpringBoot yml配置文件编写智能提示 begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- SpringBoot yml配置文件编写智能提示 end-->
<!-- 阿里JSON解析器 begin-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!-- 阿里JSON解析器 end-->
<!--常用工具类 begin-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!--常用工具类 end-->
<!-- servlet包 begin-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!-- servlet包 end-->
<!-- Spring框架基本的核心工具 begin-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- Spring框架基本的核心工具 end-->
<!-- SpringWeb模块 begin-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- SpringWeb模块 end-->
</dependencies>
1.3 Gangbb-admin的pom.xml
<dependencies>
<!-- SpringBoot Web容器 begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--以下是排除的组件-->
<exclusions>
<!--排除默认自带的log组件-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- SpringBoot Web容器 end-->
<!--项目core核心类 begin-->
<dependency>
<groupId>com.gangbb</groupId>
<artifactId>Gangbb-core</artifactId>
</dependency>
<!--项目core核心类 end-->
</dependencies>
注解要删除上节使用的:
<!--项目common通用工具类 begin(下一章该文件删除该依赖)-->
<dependency>
<groupId>com.gangbb</groupId>
<artifactId>Gangbb-common</artifactId>
</dependency>
<!--项目common通用工具类 end-->
因为导入的Gangbb-core
中已经有Gangbb-common
,不删除会导致循环依赖。
2. 创建数据库表
表名和其中一些字段名和若依的不一样
CREATE TABLE `sys_operation_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '日志主键id',
`title` varchar(255) DEFAULT '' COMMENT '操作模块',
`business_type` tinyint(4) unsigned DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)',
`class_method` varchar(255) DEFAULT '' COMMENT '请求处理方法',
`request_method` varchar(255) DEFAULT '' COMMENT '请求方式',
`operator_type` tinyint(4) unsigned DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)',
`operation_name` varchar(50) DEFAULT '' COMMENT '操作人员',
`operation_url` varchar(150) DEFAULT '' COMMENT '请求url',
`operation_ip` varchar(20) DEFAULT '' COMMENT '操作ip',
`operation_location` varchar(255) DEFAULT '' COMMENT '操作地点',
`operation_param` varchar(255) DEFAULT '' COMMENT '请求参数',
`json_result` varchar(255) DEFAULT '' COMMENT '返回参数',
`operation_status` tinyint(4) unsigned DEFAULT '0' COMMENT '操作状态(0正常 1异常)',
`error_msg` varchar(255) DEFAULT '' COMMENT '错误消息',
`operation_time` datetime DEFAULT NULL COMMENT '操作时间',
`creator` varchar(100) DEFAULT '' COMMENT '创建者',
`createTime` datetime DEFAULT NULL COMMENT '创建时间',
`reviser` varchar(100) DEFAULT '' COMMENT '更新者',
`updateTime` datetime DEFAULT NULL COMMENT '更新时间',
`isDel` tinyint(4) unsigned DEFAULT '0' COMMENT '是否删除 0-未删除 1-已删除',
`remark` varchar(255) DEFAULT '' COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
3. 自定义注解@Log及相关枚举
在 common通用工具模块 中定义:
一、Log
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
}
二、BusinessType
public enum BusinessType
{
/**
* 其它
*/
OTHER,
/**
* 新增
*/
INSERT,
/**
* 修改
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 授权
*/
GRANT,
/**
* 导出
*/
EXPORT,
/**
* 导入
*/
IMPORT,
/**
* 强退
*/
FORCE,
/**
* 生成代码
*/
GENCODE,
/**
* 清空数据
*/
CLEAN,
}
三、OperatorType
public enum OperatorType
{
/**
* 其它
*/
OTHER,
/**
* 后台用户
*/
MANAGE,
/**
* 手机端用户
*/
MOBILE
}
3. AOP切面处理请求操作日志
本节代码涉及过长不再贴出
整体思路:切入带有@Log的Controller,记录相应的请求信息。
需要先定义本节需要的一些实体类、枚举和工具类:
(其中有互相引用的,全部引入项目就不会错)
BaseEntity
所有entity实体类都要继承的基类,SysOperationLog
操作日志记录表 对应数据库(sys_operation_log)表BusinessStatus
操作状态枚举BusinessType
业务操作类型 枚举OperatorType
操作人类别 枚举StringUtils
字符串工具类CharsetKit
字符集工具类Convert
类型转换器StrFormatter
字符串格式化工具IpUtils
获取IP工具类EscapeUtil
转义和反转义工具类HTMLFilter
HTML过滤器,用于去除XSS漏洞隐患ServletUtils
客户端工具类AddressUtils
获取地址类HttpUtils
通用http发送方法Constants
通用常量信息GangbbConfig
读取项目相关配置AsyncManager
异步任务管理器Threads
线程相关工具类SpringUtils
spring工具类 方便在非spring管理环境中获取beanThreadPoolConfig
线程池配置AsyncFactory
异步工厂,产生任务用(目前只定义了 记录操作日志信息recordOperation
方法,记录登录信息recordLogininfor
方法后面到登录再涉及)
核心的LogAspect
切面处理类中的保存Log具体处理方法handleLog
主要逻辑:
- 获得注解
- 从缓存获取当前的用户信息(暂缺,先模拟一个)
- 获取请求各项需要记录的基本信息
- 判断请求是否有异常
- 保存到数据库
- 打印到控制台
既然最后一步要操数据库当然需要各层的文件:
5. log4j2的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--
6个优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出
设置为OFF 表示不记录log4j2本身的日志,
-->
<!-- status:用来指定log4j本身的打印日志级别,monitorInterval:指定log4j自动重新配置的监测间隔时间 -->
<Configuration status="fatal">
<Properties>
<Property name="baseDir" value="${sys:user.home}/logs/Gangbb_Vue"/>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="debug" onMatch="ACCEPT"
onMismatch="DENY"/>
<PatternLayout
pattern="[%d{MM:dd HH:mm:ss.SSS}] [%level] [%logger{36}] - %msg%n"/>
</Console>
<!--debug级别日志文件输出-->
<RollingFile name="debug_appender" fileName="${baseDir}/debug.log"
filePattern="${baseDir}/debug_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在debug及以上在info以下 -->
<ThresholdFilter level="debug"/>
<ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<!-- 策略 -->
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- info级别日志文件输出 -->
<RollingFile name="info_appender" fileName="${baseDir}/info.log"
filePattern="${baseDir}/info_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在info及以上在error以下 -->
<ThresholdFilter level="info"/>
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<!-- 策略 -->
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- error级别日志文件输出 -->
<RollingFile name="error_appender" fileName="${baseDir}/error.log"
filePattern="${baseDir}/error_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在error及以上 -->
<ThresholdFilter level="error"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
</Appenders>
<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
<!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
<!--监控系统信息-->
<!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
<Loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.mybatis" level="info" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<Logger name="org.springframework" level="WARN" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
<!-- 设置com.zaxxer.hikari包下的日志只打印WARN及以上级别的日志 -->
<logger name="com.zaxxer.hikari" level="WARN" additivity="false">
<appender-ref ref="Console"/>
</logger>
<!-- 设置org.hibernate.validator包下的日志只打印WARN及以上级别的日志 -->
<logger name="org.hibernate.validator" level="WARN" additivity="false">
<appender-ref ref="Console"/>
</logger>
<!-- 设置org.apache包下的日志只打印WARN及以上级别的日志 -->
<!-- <logger name="org.apache" level="WARN" additivity="false">-->
<!-- <appender-ref ref="Console"/>-->
<!-- </logger>-->
<Root level="debug">
<AppenderRef ref="Console"/>
<AppenderRef ref="debug_appender"/>
<AppenderRef ref="info_appender"/>
<AppenderRef ref="error_appender"/>
</Root>
</Loggers>
</Configuration>
看到控制台有什么不爽的打印信息,在 标签对内再写一个<logger …>过滤就行了!
6. 修改Mybatis配置
- 添加:
MyBatisConfig
使 Mybatis支持*匹配扫描包 - Gangbb-admin的
application.yml
:
mybatis:
configuration:
# 实体类驼峰命名转成数据库中的下划线命名
map-underscore-to-camel-case: true
# 指定 MyBatis 所用日志的具体实现
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
# 全局映射器启用缓存
cache-enabled: true
# 允许 JDBC 支持自动生成主键
use-generated-keys: true
# 配置默认的执行器
default-executor-type: reuse
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapper-locations: classpath*:mapper/**/*Mapper.xml
# 扫描实体类路径(搜索指定包别名)
type-aliases-package: com.gangbb.**.entity
-
ApplicationConfig
配置对dao层mapper的扫描。
@Configuration
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("com.gangbb.**.dao")
public class ApplicationConfig
{
/**
* 时区配置
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
{
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
}
}
6. 测试日志记录
新建测试TestLogAnnotationController
@RestController
@RequestMapping("log")
public class TestLogAnnotationController {
@Log(title = "测试@Log注解", businessType = BusinessType.OTHER)
@GetMapping("/hello")
public String HelloLog(){
return "测试@Log注解,记录操作日志";
}
}
发送请求:
日志控制台打印信息:
(是不是十分清晰明了,i了i了!)
数据库中也有相应的数据: