谷粒学院项目实战——03后台讲师管理模块(一)后端

一. 后台讲师管理模块

1. 查询相关的功能

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层
  1. 编写EduTeacherService

    public interface EduTeacherService extends IService<EduTeacher> {
        // 分页查询讲师
        public Map<String, Object> getTeacherFrontList(Page<EduTeacher> pageTeacher);
    
    }
    
  2. 编写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层
  1. 编写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层
  1. 创建SpringBoot配置类

    @Configuration
    @MapperScan("com.hjf.eduservice.mapper")
    public class EduConfig {
    }
    
5. 启动类
  1. 创建SpringBoot启动类

    @SpringBootApplication
    public class EduApplication {
        public static void main(String[] args) {
            SpringApplication.run(EduApplication.class, args);
        }
    }
    
6. 展示
  1. 代码目录结构
    image-20210410000037725

  2. 测试
    image-20200930082303820

2. 通过id查询讲师
  1. 代码

    // 根据讲师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. 分页查询
  1. 分页的数据格式

    {
      "success": true,
      "code": 20000,
      "message": "成功",
      "data": {
        "total": 17,
        "rows": [
          {
            "id": "1",
            "name": "刘德华",
            "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"
          }
        ]
      }
    }
    
  2. service_eduEduConfig配置文件中配置Mybatis-plus分页插件

    // 分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
    
  3. 编写讲师分页查询的方法

    // 分页查询讲师信息
    // 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);
    }
    
  4. 效果

    image-20200930233931010
3. 多条件组合查询带分页
  1. 创建查询对象类

    image-20200930235558623
    @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;
    }
    
  2. 编写条件查询的方法

    // 条件查询带分页的方法
    @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. 逻辑删除
  1. 添加逻辑删除的插件

    // 逻辑删除
    @Bean
    public ISqlInjector sqlInjector(){
        return new LogicSqlInjector();
    }
    
    image-20210410144321323
  2. 在实体类的逻辑删除属性上面添加注解

    @ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")
    @TableLogic     // 逻辑删除注解
    private Boolean isDeleted;
    
    image-20210410144552340
2. 在已由的方法上加上Swagger
  1. 代码

    @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;
        }
    }
    
  2. 结果

    image-20200930213836628

3. 添加相关的功能

1. 添加讲师
  1. 自动填充

    见下面的自动填充

  2. 代码

    // 添加讲师接口的方法
    @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.修改讲师
  1. 根据讲师id进行查询

    见前面的方法

  2. 通过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

  1. 创建common-> service_base模块

  2. 子模块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>
    
  3. 在子子模块中创建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();
        }
    }
    
    
  4. 具体的目录结构

    image-20200930173654160

2. 引用公共模块(Swagger模块)

  1. 在子模块service中引入Swagger模块的依赖

    <!--引入公共依赖-->
    <dependency>
        <groupId>com.hjf</groupId>
        <artifactId>service_base</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    
  2. 填写的依据

    image-20200930174023017
  3. service_base启动类中添加ComponentScan注解

    @SpringBootApplication
    /**
     * 扫描指定路径下的包
     * 如果不添加注解, 启动器只能扫描当前项目中的类, 别的项目中的类扫描不到
     * 这样公共类中的配置也就使用不了了
     */
    @ComponentScan(basePackages = {"com.hjf"})  
    public class EduApplication {
        public static void main(String[] args) {
            SpringApplication.run(EduApplication.class, args);
        }
    }
    
    

3. 访问

  1. 网址: http://localhost:8001/swagger-ui.html

    image-20210410011206101

五. 统一结果返回

1. 数据格式

1. 不同类型的格式
  1. 列表

    {
      "success": true,
      "code": 20000,
      "message": "成功",
      "data": {
        "items": [
          {
            "id": "1",
            "name": "刘德华",
            "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"
          }
        ]
      }
    }
    
  2. 分页

    {
      "success": true,
      "code": 20000,
      "message": "成功",
      "data": {
        "total": 17,
        "rows": [
          {
            "id": "1",
            "name": "刘德华",
            "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"
          }
        ]
      }
    }
    
  3. 没有数据返回

    {
      "success": true,
      "code": 20000,
      "message": "成功",
      "data": {}
    }
    
  4. 错误返回

    {
      "success": false,
      "code": 20001,
      "message": "失败",
      "data": {}
    }
    
  5. 统一格式

    {
      "success": 布尔, //响应是否成功
      "code": 数字, //响应码
      "message": 字符串, //返回消息
      "data": HashMap //返回数据,放在键值对中
    }
    

2. 定义

  1. common模块中创建一个子模块common_utils

  2. 创建interface, 定义数据返回状态码[定义常量]

    public interface ResultCode {
        public static Integer SUCCESS = 20000; // 成功
        public static Integer ERROR = 20001; // 失败
    }
    
  3. 定义具体的返回格式

    返回值类型

    ​ {

    ​ “success”: 布尔, //响应是否成功

    ​ “code”: 数字, //响应码

    ​ “message”: 字符串, //返回消息

    ​ “data”: HashMap //返回数据,放在键值对中

    ​ }

  4. 返回值类型类

    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. 使用统一结果返回

  1. commonutils模块引入到service模块中

    <dependency>
        <groupId>com.hjf</groupId>
        <artifactId>common_utils</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    
  2. 把接口的返回值类型都是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();
            }
        }
    }
    
  3. 格式化后的结果
    image-20200930225007649

六. 自动填充

1. 自动填充封装

1. 在service_base模块中创建hander包, 并创建MyMetaObjectHandler类

为了能通用, 所以创建再公共模块中

  1. 具体路径

    image-20201001083652766
  2. 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);
        }
    }
    
  3. 在实体类中对应的属性前添加自动填充的注解

    @ApiModelProperty(value = "创建时间")
    // 创建时自动填充
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;
    
    @ApiModelProperty(value = "更新时间")
    // 创建和修改时自动填充
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;
    

    image-20210410151321503

  4. 要想生效, 需要在主启动类中添加扫描的注解

    // 之前已经添加了, 这里不用再添加了
    @ComponentScan(basePackages = {"com.hjf"})
    

七. 统一异常处理

我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处理

1. 自带异常处理类

在service-base中创建统一异常处理类GlobalExceptionHandler.java:

  1. 具体路径

    image-20201001101606910
  2. 在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就可以注释掉了

  3. 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:

  1. 具体路径

    image-20201001105650376
  2. 创建自定义异常类继承RuntimeException, 并写属性

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class GuliException extends RuntimeException {
        @ApiModelProperty("状态码")
        private Integer code;
        @ApiModelProperty("异常信息")
        private String msg;
    }
    
  3. 在GlobalExceptionHandler统一异常类中添加

    // 自定义异常
    @ExceptionHandler(GuliException.class)
    @ResponseBody      // 为了能返回数据
    public ReturnType error(GuliException e){
        e.printStackTrace();
        return ReturnType.error().code(e.getCode()).message(e.getMsg());
    }
    
    image-20210410155129917
  4. 执行自定义异常

    try {
        int i = 10 / 0;
    } catch (Exception e) {
        // 执行自定义异常
        throw new GuliException(ResultCode.ERROR, "执行了自定义异常处理");
    }
    
  5. 运行结果
    image-20201001110326335
    image-20210410155209491

八. 统一日志处理

1. 日志

1. 日志级别

OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL

2. 设置日志级别
# 设置日志级别
# 默认 INFO
logging.level.root = INFO

2. Logback日志工具

把日志不仅输出到控制台, 也可以输出到文件中.

  1. 删除application.properties中日志的配置

    # mybatis日志
    # mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    #
    # 设置日志级别
    # logging.level.root = warn
    
  2. 在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>
    
  3. 启动项目
    控制台
    image-20201001112201088
    本地文件
    image-20201001112256831

  4. 将异常原因也写入文件

  5. 在GlobalExceptionHandler中添加@Slf4j注解

    image-20201001112858742
  6. 在自定义异常中加上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());
    	}
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值