一. 后台讲师管理模块
1. 查询相关的功能
1. 查询所有讲师
1. 配置文件
-
配置文件内容
# 服务端口号 server.port=8001 # 服务名 spring.application.name=service_edu # 环境设置: dev, test, prod spring.profiles.active=dev # 数据库连接 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://www.jiangfengtime.top:3306/guli?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=123456 # mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # 返回json的全局时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8
2. Service层
-
编写EduTeacherService
public interface EduTeacherService extends IService<EduTeacher> { // 分页查询讲师 public Map<String, Object> getTeacherFrontList(Page<EduTeacher> pageTeacher); }
-
编写EduTeacherServiceImpl
@Service public class EduTeacherServiceImpl extends ServiceImpl<EduTeacherMapper, EduTeacher> implements EduTeacherService { @Override public Map<String, Object> getTeacherFrontList(Page<EduTeacher> pageTeacher) { QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>(); // 按照id进行降序排序 wrapper.orderByDesc("id"); // 把分页数据封装到对象 baseMapper.selectPage(pageTeacher, wrapper); List<EduTeacher> records = pageTeacher.getRecords(); // 每页数据的list集合 long current = pageTeacher.getCurrent(); // 当前页 long pages = pageTeacher.getPages(); // 总页数 long size = pageTeacher.getSize(); // 每页记录数 long total = pageTeacher.getTotal(); // 总记录数 boolean hasNext = pageTeacher.hasNext(); // 是否有下一页 boolean hasPrevious = pageTeacher.hasPrevious(); // 是否有上一页 // 把分页数据获取出来, 放到Map集合中 HashMap<String, Object> map = new HashMap<>(); map.put("items", records); map.put("current", current); map.put("pages", pages); map.put("size", size); map.put("total", total); map.put("hasNext", hasNext); map.put("hasPrevious", hasPrevious); return map; } }
3.Controller层
-
编写controller代码
@RestController @RequestMapping("/eduservice/teacher") public class EduTeacherController { @Autowired private EduTeacherService teacherService; // 查询讲师表中的所有数据 // rest风格 @GetMapping("/findAll") public List<EduTeacher> findAllTeacher(){ // 调用Service方法, 实现查询所有的操作 List<EduTeacher> list = teacherService.list(null); return list; } }
4. Config层
-
创建SpringBoot配置类
@Configuration @MapperScan("com.hjf.eduservice.mapper") public class EduConfig { }
5. 启动类
-
创建SpringBoot启动类
@SpringBootApplication public class EduApplication { public static void main(String[] args) { SpringApplication.run(EduApplication.class, args); } }
6. 展示
-
代码目录结构
-
测试
2. 通过id查询讲师
-
代码
// 根据讲师id进行查询 @GetMapping("/getTeacher/{id}") @ApiOperation(value = "根据讲师id查询") public ReturnType getTeacher(@PathVariable String id){ EduTeacher eduTeacher = teacherService.getById(id); return ReturnType.ok().data("teacher", eduTeacher); }
2. 分页查询
-
分页的数据格式
{ "success": true, "code": 20000, "message": "成功", "data": { "total": 17, "rows": [ { "id": "1", "name": "刘德华", "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余" } ] } }
-
在
service_edu
的EduConfig
配置文件中配置Mybatis-plus分页插件// 分页插件 @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); }
-
编写讲师分页查询的方法
// 分页查询讲师信息 // current: 当前页; limit: 每页记录数 @GetMapping("/pageTeacher/{current}/{limit}") @ApiOperation(value = "分页查询讲师") public ReturnType pageTeacher(@ApiParam(name = "current", value = "当前页", required = true) @PathVariable Integer current, @ApiParam(name = "limit", value = "页面大小", required = true) @PathVariable Integer limit){ // 1. 创建一个page对象 Page<EduTeacher> pageTeacher = new Page<>(current, limit); // 2. 调用方法实现分页 // 调用方法时候, 底层封装, 把分页所有数据封装到pageTeacher对象里面 teacherService.page(pageTeacher, null); long total = pageTeacher.getTotal(); // 总记录数 List<EduTeacher> records = pageTeacher.getRecords(); // 数据list集合 // Map<String, Object> map = new HashMap<>(); // map.put("total", total); // map.put("rows", records); // return ReturnType.ok().data(map); return ReturnType.ok().data("total", total).data("rows", records); }
-
效果
3. 多条件组合查询带分页
-
创建查询对象类
@Data public class TeacherQuery { @ApiModelProperty(value = "教师名称, 模糊查询") private String name; @ApiModelProperty(value = "头衔, 1: 高级讲师; 2: 首席讲师") private Integer level; @ApiModelProperty(value = "查询开始事件" , example = "2020-01-01 01:32:21") private String begin; // 这里使用String类型, 前端传过来的数据无需进行类型转换 @ApiModelProperty(value = "查询结束事件" , example = "2020-03-01 01:32:21") private String end; }
-
编写条件查询的方法
// 条件查询带分页的方法 @PostMapping("/pageTeacherCondition/{current}/{limit}") @ApiOperation(value = "条件查询讲师(带分页)") public ReturnType pageTeacherCondition( @ApiParam(value = "当前页", required = true) @PathVariable Integer current, @ApiParam(value = "页面大小", required = true) @PathVariable Integer limit, // @RequestBody: 使用json传递数据, 把json数据封装到对应的对象里 // 此时必须使用post方式提交, 否则获取不到提交的信息 @ApiParam(value = "查询条件类") @RequestBody(required = false) TeacherQuery teacherQuery){ // 创建Page对象 Page<EduTeacher> pageTeacher = new Page<>(current, limit); //根据条件进行判断, 按条件拼接SQL // 构建条件, 多条件组合查询 QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>(); // 判断条件值是否为空, 如果不为空拼接条件 String name = teacherQuery.getName(); Integer level = teacherQuery.getLevel(); String begin = teacherQuery.getBegin(); String end = teacherQuery.getEnd(); if (!StringUtils.isEmpty(name)) { // 构建条件 wrapper.like("name", name); } if (!StringUtils.isEmpty(level)){ wrapper.eq("level", level); } if (!StringUtils.isEmpty(begin)) { // 这里的字段时表总的字段名, 而不是类总的属性名 wrapper.ge("gmt_create", begin); } if (!StringUtils.isEmpty(end)) { // 这里的字段时表总的字段名, 而不是类总的属性名 wrapper.le("gmt_create", end); } // 调用方法实现条件查询分页 teacherService.page(pageTeacher, wrapper); long total = pageTeacher.getTotal(); List<EduTeacher> records = pageTeacher.getRecords(); return ReturnType.ok().data("total", total).data("rows", records); }
2. 删除相关的功能
1. 逻辑删除
-
添加逻辑删除的插件
// 逻辑删除 @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); }
-
在实体类的逻辑删除属性上面添加注解
@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除") @TableLogic // 逻辑删除注解 private Boolean isDeleted;
2. 在已由的方法上加上Swagger
-
代码
@Api(description = "讲师管理") // 在类上加注解 @RestController @RequestMapping("/eduservice/teacher") public class EduTeacherController { @Autowired private EduTeacherService teacherService; // 查询讲师表中的所有数据 // rest风格 @GetMapping("/findAll") @ApiOperation(value = "所有讲师列表") public List<EduTeacher> findAllTeacher(){ // 调用Service方法, 实现查询所有的操作 List<EduTeacher> list = teacherService.list(null); return list; } // id 需要通过路径传 @DeleteMapping("/del/{id}") @ApiOperation(value = "逻辑删除讲师") // @ApiParam(name = "id", value = "讲师id", required = true) 在参数上加注解 public boolean removeTeacher( @ApiParam(name = "id", value = "讲师id", required = true) @PathVariable("id") String id){ boolean flag = teacherService.removeById(id); return flag; } }
-
结果
3. 添加相关的功能
1. 添加讲师
-
自动填充
见下面的自动填充
-
代码
// 添加讲师接口的方法 @PostMapping("/addTeacher") @ApiOperation(value = "添加讲师") public ReturnType addTeacher(@RequestBody EduTeacher eduTeacher){ boolean save = teacherService.save(eduTeacher); if (save){ return ReturnType.ok(); } else { return ReturnType.error(); } }
4. 修改相关的功能
1.修改讲师
-
根据讲师id进行查询
见前面的方法
-
通过id修改
// 讲师修改功能 // json数据中必须包含id @PostMapping("/updateTeacher") @ApiOperation(value = "讲师修改") public ReturnType updateTeacher(@RequestBody EduTeacher eduTeacher){ boolean update = teacherService.updateById(eduTeacher); if (update) { return ReturnType.ok(); } else { return ReturnType.error(); } }
四. 创建Swagger
以为所有的模块都需要用到Swagger, 所以选择在公共模块中配置Swagger
1. 创建Swagger
-
创建
common
->service_base
模块 -
子模块
common
中添加如下依赖<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <scope>provided </scope> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <scope>provided </scope> </dependency> <!--lombok用来简化实体类:需要安装lombok插件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided </scope> </dependency> <!--swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <scope>provided </scope> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <scope>provided </scope> </dependency> <!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- spring2.X集成redis所需common-pool2 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.0</version> </dependency>--> </dependencies>
-
在子子模块中创建Swagger的配置类
SwaggerConfig
@Configuration // 声明配置类 @EnableSwagger2 // 启动Swagger注解 public class SwaggerConfig { @Bean public Docket webApiConfig(){ return new Docket(DocumentationType.SWAGGER_2) .groupName("webApi") // 配置Swagger配置信息 .apiInfo(webApiInfo()) .select() // 不扫描/admin和/error下的内容 .paths(Predicates.not(PathSelectors.regex("/admin/.*"))) .paths(Predicates.not(PathSelectors.regex("/error.*"))) .build(); } // 配置Swagger配置信息 private ApiInfo webApiInfo() { return new ApiInfoBuilder() .title("网站-课程中心API文档") .description("本文档描述了课程中心微服务接口定义") .version("1.0") .contact(new Contact("Helen", "http://www.jiangfengtime.top", "1344602850@qq.com")) .build(); } }
-
具体的目录结构
2. 引用公共模块(Swagger模块)
-
在子模块
service
中引入Swagger模块的依赖<!--引入公共依赖--> <dependency> <groupId>com.hjf</groupId> <artifactId>service_base</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
-
填写的依据
-
service_base
启动类中添加ComponentScan
注解@SpringBootApplication /** * 扫描指定路径下的包 * 如果不添加注解, 启动器只能扫描当前项目中的类, 别的项目中的类扫描不到 * 这样公共类中的配置也就使用不了了 */ @ComponentScan(basePackages = {"com.hjf"}) public class EduApplication { public static void main(String[] args) { SpringApplication.run(EduApplication.class, args); } }
3. 访问
-
网址: http://localhost:8001/swagger-ui.html
五. 统一结果返回
1. 数据格式
1. 不同类型的格式
-
列表
{ "success": true, "code": 20000, "message": "成功", "data": { "items": [ { "id": "1", "name": "刘德华", "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余" } ] } }
-
分页
{ "success": true, "code": 20000, "message": "成功", "data": { "total": 17, "rows": [ { "id": "1", "name": "刘德华", "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余" } ] } }
-
没有数据返回
{ "success": true, "code": 20000, "message": "成功", "data": {} }
-
错误返回
{ "success": false, "code": 20001, "message": "失败", "data": {} }
-
统一格式
{ "success": 布尔, //响应是否成功 "code": 数字, //响应码 "message": 字符串, //返回消息 "data": HashMap //返回数据,放在键值对中 }
2. 定义
-
在
common
模块中创建一个子模块common_utils
-
创建interface, 定义数据返回状态码[定义常量]
public interface ResultCode { public static Integer SUCCESS = 20000; // 成功 public static Integer ERROR = 20001; // 失败 }
-
定义具体的返回格式
返回值类型
{
“success”: 布尔, //响应是否成功
“code”: 数字, //响应码
“message”: 字符串, //返回消息
“data”: HashMap //返回数据,放在键值对中
}
-
返回值类型类
package com.hjf.commonutils; import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiParam; import lombok.Data; import java.util.HashMap; import java.util.Map; /** * 统一返回结果类型 * * 返回值类型 * { * "success": 布尔, //响应是否成功 * "code": 数字, //响应码 * "message": 字符串, //返回消息 * "data": HashMap //返回数据,放在键值对中 * } */ @Data public class ReturnType { @ApiModelProperty(value = "是否成功") private Boolean success; @ApiModelProperty(value = "响应码") private Integer code; @ApiModelProperty(value = "返回消息") private String message; @ApiModelProperty(value = "返回数据") private Map<String, Object> data = new HashMap<>(); // 把构造方法私有 private ReturnType(){}; // 成功静态方法 public static ReturnType ok(){ ReturnType returnType = new ReturnType(); returnType.setSuccess(true); returnType.setCode(ResultCode.SUCCESS); returnType.setMessage("成功"); return returnType; } // 失败静态方法 public static ReturnType error(){ ReturnType returnType = new ReturnType(); returnType.setSuccess(false); returnType.setCode(ResultCode.ERROR); returnType.setMessage("失败"); return returnType; } public ReturnType success(Boolean success) { this.setMessage(message); return this; // 当前类的对象 } public ReturnType message(String message){ this.setMessage(message); return this; } public ReturnType code(Integer code){ this.setCode(code); return this; } public ReturnType data(String key, Object value) { this.data.put(key, value); return this; } public ReturnType data(Map<String, Object> map) { this.setData(map); return this; } }
3. 使用统一结果返回
-
将
commonutils
模块引入到service
模块中<dependency> <groupId>com.hjf</groupId> <artifactId>common_utils</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
-
把接口的返回值类型都是ReturnType
@Api(description = "讲师管理") @RestController @RequestMapping("/eduservice/teacher") public class EduTeacherController { @Autowired private EduTeacherService teacherService; @GetMapping("/findAll") @ApiOperation(value = "所有讲师列表") // 返回值类型改为统一返回类型 public ReturnType findAllTeacher(){ List<EduTeacher> list = teacherService.list(null); // 链式编程 return ReturnType.ok().data("items", list); } @DeleteMapping("/del/{id}") @ApiOperation(value = "逻辑删除讲师") // 返回值类型改为统一返回类型 public ReturnType removeTeacher( @ApiParam(name = "id", value = "讲师id", required = true) @PathVariable("id") String id){ boolean flag = teacherService.removeById(id); if (flag) { return ReturnType.ok(); } else { return ReturnType.error(); } } }
-
格式化后的结果
六. 自动填充
1. 自动填充封装
1. 在service_base模块中创建hander包, 并创建MyMetaObjectHandler类
为了能通用, 所以创建再公共模块中
-
具体路径
-
MyMetaObjectHandler类
@Component public class MyMetaObjectHandler implements MetaObjectHandler { // 插入时, 自动填充创建时间和修改时间 @Override public void insertFill(MetaObject metaObject) { // 填的是实体类中的属性名, 而不是数据表中的字段名 this.setFieldValByName("gmtCreate", new Date(), metaObject); this.setFieldValByName("gmtModified", new Date(), metaObject); } // 修改时, 自动填充修改时间 @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("gmtModified", new Date(), metaObject); } }
-
在实体类中对应的属性前添加自动填充的注解
@ApiModelProperty(value = "创建时间") // 创建时自动填充 @TableField(fill = FieldFill.INSERT) private Date gmtCreate; @ApiModelProperty(value = "更新时间") // 创建和修改时自动填充 @TableField(fill = FieldFill.INSERT_UPDATE) private Date gmtModified;
-
要想生效, 需要在主启动类中添加扫描的注解
// 之前已经添加了, 这里不用再添加了 @ComponentScan(basePackages = {"com.hjf"})
七. 统一异常处理
我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处理
1. 自带异常处理类
在service-base中创建统一异常处理类GlobalExceptionHandler.java:
-
具体路径
-
在service_base中引入统一的结果返回
<dependency> <groupId>com.hjf</groupId> <artifactId>common_utils</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
注意: 因为在
service_base
中已经引入了common_utils
, 所以在service
模块中引入的common_utils
就可以注释掉了 -
GlobalExceptionHandler
// 统一异常处理类 // 优先找特定异常, 最后再全局异常 @ControllerAdvice public class GlobalExceptionHandler { // 指定出现扫描异常执行这个方法 @ExceptionHandler(Exception.class) @ResponseBody // 为了能返回数据 public ReturnType error(Exception e){ e.printStackTrace(); return ReturnType.error().message("执行Exception异常处理..."); } // 特定异常 // 指定出现扫描异常执行这个方法 @ExceptionHandler(ArithmeticException.class) @ResponseBody // 为了能返回数据 public ReturnType error(ArithmeticException e){ e.printStackTrace(); return ReturnType.error().message("执行ArithmeticException异常处理..."); } }
2. 自定义异常处理
在service-base中创建统一异常处理类GuliException.java:
-
具体路径
-
创建自定义异常类继承RuntimeException, 并写属性
@Data @AllArgsConstructor @NoArgsConstructor public class GuliException extends RuntimeException { @ApiModelProperty("状态码") private Integer code; @ApiModelProperty("异常信息") private String msg; }
-
在GlobalExceptionHandler统一异常类中添加
// 自定义异常 @ExceptionHandler(GuliException.class) @ResponseBody // 为了能返回数据 public ReturnType error(GuliException e){ e.printStackTrace(); return ReturnType.error().code(e.getCode()).message(e.getMsg()); }
-
执行自定义异常
try { int i = 10 / 0; } catch (Exception e) { // 执行自定义异常 throw new GuliException(ResultCode.ERROR, "执行了自定义异常处理"); }
-
运行结果
八. 统一日志处理
1. 日志
1. 日志级别
OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL
2. 设置日志级别
# 设置日志级别
# 默认 INFO
logging.level.root = INFO
2. Logback日志工具
把日志不仅输出到控制台, 也可以输出到文件中.
-
删除application.properties中日志的配置
# mybatis日志 # mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # # 设置日志级别 # logging.level.root = warn
-
在common_utils模块的resources中创建logback-spring.xml文件
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="10 seconds"> <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 --> <!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true --> <!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --> <!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <contextName>logback</contextName> <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 --> <property name="log.path" value="E:/log/guli_log/edu" /> <!-- 彩色日志 --> <!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 --> <!-- magenta:洋红 --> <!-- boldMagenta:粗红--> <!-- cyan:青色 --> <!-- white:白色 --> <!-- magenta:洋红 --> <property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/> <!--输出到控制台--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息--> <!-- 例如:如果此处配置了INFO级别,则后面其他位置即使配置了DEBUG级别的日志,也不会被输出 --> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <!-- 设置字符集 --> <charset>UTF-8</charset> </encoder> </appender> <!--输出到文件--> <!-- 时间滚动输出 level为 INFO 日志 --> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文件的路径及文件名 --> <file>${log.path}/log_info.log</file> <!--日志文件输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每天日志归档路径以及格式 --> <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文件只记录info级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 时间滚动输出 level为 WARN 日志 --> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文件的路径及文件名 --> <file>${log.path}/log_warn.log</file> <!--日志文件输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文件只记录warn级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 时间滚动输出 level为 ERROR 日志 --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文件的路径及文件名 --> <file>${log.path}/log_error.log</file> <!--日志文件输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文件只记录ERROR级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- <logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。 <logger>仅有一个name属性, 一个可选的level和一个可选的addtivity属性。 name:用来指定受此logger约束的某一个包或者具体的某一个类。 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 如果未设置此属性,那么当前logger将会继承上级的级别。 --> <!-- 使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作: 第一种把<root level="INFO">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息 第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别: --> <!--开发环境:打印控制台--> <springProfile name="dev"> <!--可以输出项目中的debug日志,包括mybatis的sql日志--> <logger name="com.guli" level="INFO" /> <!-- root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG 可以包含零个或多个appender元素。 --> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> </springProfile> <!--生产环境:输出到文件--> <springProfile name="pro"> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="WARN_FILE" /> </root> </springProfile> </configuration>
-
启动项目
控制台
本地文件
-
将异常原因也写入文件
-
在GlobalExceptionHandler中添加
@Slf4j
注解 -
在自定义异常中加上
log.error(e.getMessage());
@ExceptionHandler(GuliException.class) @ResponseBody // 为了能返回数据 public ReturnType error(GuliException e){ // 会将异常信息写入到日志文件中 log.error(e.getMsg()); e.printStackTrace(); return ReturnType.error().code(e.getCode()).message(e.getMsg()); }