尚医通(三)医院设置模块后端 | swagger | 统一日志 | 统一返回结果

news2025/1/16 16:57:17

目录

  • 一、医院设置模块需求
  • 二、医院设置表结构
  • 三、医院模块配置
  • 四、医院查询功能
    • 1、创建包结构,创建SpringBoot启动类
    • 2、编写controller代码
    • 3、创建SpringBoot配置类
    • 5、运行启动类
    • 6、统一返回的json时间格式
  • 五、医院设置逻辑删除功能
    • 1、HospitalSetController添加删除方法
    • 2、使用postman测试删除
  • 六、配置Swagger2生成API接口文档
    • 1、创建common模块
    • 2、在common中引入相关依赖
    • 3、在common下面创建子模块service_utils
    • 4、在模块service-utils中,创建swagger的配置类
    • 5、在模块service模块中引入service_utils
    • 6、在service_hosp启动类上添加注解,进行测试
    • 7、通过地址访问测试
    • 8、定义接口说明和参数说明
  • 七、统一返回数据格式
    • 1、在子模块service_utils创建接口定义返回码
    • 2、创建结果类
    • 3、修改Controller中的返回结果
  • 八、分页和条件查询
    • 1、HospPlusConfig中配置分页插件
    • 2、条件分页Controller方法
    • 3、Controller方法
    • 4、Swagger中测试
  • 九、新增和修改医院设置工具
    • 1、新增
    • 2、根据id查询
    • 3、根据id修改
  • 十、批量删除医院设置和锁定医院设置接口
    • 1、批量删除
    • 2、锁定和解锁
  • 十一、统一异常处理
    • 1、创建统一异常处理器
    • 2、添加异常处理方法
    • 3、测试
    • 4、自定义异常类
  • 十二、统一日志
    • 1、配置日志级别
    • 2、Logback日志
    • 3、配置logback日志
    • 4、将错误日志输出到文件

一、医院设置模块需求

医院设置主要是用来保存开通医院的一些基本信息,每个医院一条信息,保存了医院编号(平台分配,全局唯一)和接口调用相关的签名key等信息,是整个流程的第一步,只有开通了医院设置信息,才可以上传医院相关信息。我们所开发的功能就是基于单表的一个CRUD、锁定/解锁和发送签名信息这些基本功能。

二、医院设置表结构

在这里插入图片描述
hosname:医院名称
hoscode:医院编号(平台分配,全局唯一,api接口必填信息)
api_url:医院回调的基础url(如:预约下单,我们要调用该地址去医院下单)
sign_key:双方api接口调用的签名key,有平台生成
contacts_name:医院联系人姓名
contacts_phone:医院联系人手机
status:状态(锁定/解锁)

三、医院模块配置

1、在service下面service_hosp模块中创建配置文件
resources目录下创建文件 application.properties
在这里插入图片描述

# 服务端口
server.port=8201
# 服务名
spring.application.name=service-hosp

# 环境设置:dev、test、prod
spring.profiles.active=dev

# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/yygh_hosp?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

乱码解决方案
https://donglin.blog.csdn.net/article/details/125453678

四、医院查询功能

1、创建包结构,创建SpringBoot启动类

创建启动类ServiceHospApplication.java,注意启动类的创建位置
在这里插入图片描述

@SpringBootApplication
public class ServiceHospApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceHospApplication.class,args);
    }
}

2、编写controller代码

//医院设置接口
@RestController
@RequestMapping("/admin/hosp/hospitalSet")
public class HospitalSetController {

    @Autowired
    private HospitalSetService hospitalSetService;

    //查询所有医院设置
    @GetMapping("findAll")
    public List<HospitalSet> findAll() {
        List<HospitalSet> list = hospitalSetService.list();
        return list;
    }
}

3、创建SpringBoot配置类

创建config包,创建HospConfig.java

@Configuration
@EnableTransactionManagement
@MapperScan("com.donglin.yygh.hosp.mapper")
public class HospConfig {
    
}

5、运行启动类

访问http://localhost:8201/admin/hosp/hospitalSet/findAll
得到json数据
在这里插入图片描述
千万注意:将代码生成器生成的entity实体类去掉,用yygh-parent父工程下model模块的实体类。

6、统一返回的json时间格式

默认情况下json时间格式带有时区,并且是世界标准时间,和我们的时间差了八个小时
在application.properties中设置

#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

五、医院设置逻辑删除功能

1、HospitalSetController添加删除方法

@DeleteMapping("{id}")
public boolean removeById(@PathVariable String id){
    return hospitalSetService.removeById(id);
}

2、使用postman测试删除

在这里插入图片描述

测试结果:数据库中的is_deleted字段被修改为1

六、配置Swagger2生成API接口文档

Swagger2介绍
前后端分离开发模式中,api文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

  • 1.及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
  • 2.规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
  • 3.一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
  • 4.可测性 (直接在接口文档上进行测试,以方便理解业务)

配置Swagger2

1、创建common模块

在yygh_parent下创建模块common

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>
    </dependency>

    <!--swagger-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
    </dependency>
</dependencies>

3、在common下面创建子模块service_utils

4、在模块service-utils中,创建swagger的配置类

创建包com.donglin.yygh.common.config,创建类Swagger2Config

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("admin")
                .apiInfo(getAdminApiInfo())
                .select()
                //只显示api路径下的页面
                .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
                .build();
    }

    private ApiInfo getAdminApiInfo(){
        return new ApiInfoBuilder()
                .title("管理员系统")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("donglin", "http://donglin.com", "1909529369@qq.com"))
                .build();
    }

    @Bean
    public Docket userApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("user")
                .apiInfo(getUserApiInfo())
                .select()
                //只显示admin路径下的页面
                .paths(Predicates.and(PathSelectors.regex("/user/.*")))
                .build();
    }

    private ApiInfo getUserApiInfo(){
        return new ApiInfoBuilder()
                .title("用户系统")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("donglin", "http://donglin.com", "1909529369@qq.com"))
                .build();
    }

    @Bean
    public Docket apiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("api")
                .apiInfo(getApiInfo())
                .select()
                //只显示admin路径下的页面
                .paths(Predicates.and(PathSelectors.regex("/api/.*")))
                .build();
    }


    private ApiInfo getApiInfo(){
        return new ApiInfoBuilder()
                .title("Api系统")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("donglin", "http://donglin.com", "1909529369@qq.com"))
                .build();
    }



}

5、在模块service模块中引入service_utils

<dependency>
    <groupId>com.donglin</groupId>
    <artifactId>service_utils</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

6、在service_hosp启动类上添加注解,进行测试

因为swagger在common模块下,在不同模块下,我们需要使用@ComponentScan才能扫描导Swagger
在这里插入图片描述

7、通过地址访问测试

在这里插入图片描述
可以添加一些自定义设置,例如:
定义样例数据

@ApiModelProperty(value = "创建时间", example = "2019-01-01 8:00:00")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;

@ApiModelProperty(value = "更新时间", example = "2019-01-01 8:00:00")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;

8、定义接口说明和参数说明

@Api(tags=“”):标记在接口类上
@ApiOperation(value=“”):标记在方法上
@ApiParam(value=“”):标记在参数上
@ApiModel(description=“”):对POJO类做说明
@ApiModelProperty(value=“”):对POJO类属性做说明

//医院设置接口
@Api(description = "医院设置接口")
@RestController
@RequestMapping("/admin/hosp/hospitalSet")
public class HospitalSetController {

    @Autowired
    private HospitalSetService hospitalSetService;

    //查询所有医院设置
    @ApiOperation(value = "医院设置列表")
    @GetMapping("findAll")
    public List<HospitalSet> findAll() {
        List<HospitalSet> list = hospitalSetService.list();
        return list;
    }

    @ApiOperation(value = "医院设置删除")
    @DeleteMapping("{id}")
    public boolean removeById(@ApiParam(name = "id", value = "医院设置ID", required = true) @PathVariable String id){
        return hospitalSetService.removeById(id);
    }
}

七、统一返回数据格式

项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容
例如,我们的系统要求返回的基本数据格式如下:
列表:

{
  "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 //返回数据,放在键值对中
}

1、在子模块service_utils创建接口定义返回码

创建包com.donglin.yygh.common.result,创建接口 REnum .java

public enum REnum {
    SUCCESS(20000,"成功",true),
    ERROR(20001,"失败",false)
    ;
    //枚举项:
    private Integer code;
    private String message;
    private Boolean flag;

    REnum(Integer code, String message, Boolean flag) {
        this.code = code;
        this.message = message;
        this.flag = flag;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Boolean getFlag() {
        return flag;
    }

    public void setFlag(Boolean flag) {
        this.flag = flag;
    }
}


2、创建结果类

创建类 R.java

@Data
public class R {

    private Integer code;
    private Boolean success;
    private String message;
    private Map<String,Object> data = new HashMap<>();

    private R(){

    }

    public static R ok(){
        R r = new R();
        r.setCode(REnum.SUCCESS.getCode());
        r.setSuccess(REnum.SUCCESS.getFlag());
        r.setMessage(REnum.SUCCESS.getMessage());
        return r;
    }

    public static R error(){
        R r = new R();
        r.setCode(REnum.ERROR.getCode());
        r.setSuccess(REnum.ERROR.getFlag());
        r.setMessage(REnum.ERROR.getMessage());
        return r;
    }

    public R code(Integer code){
        this.code = code;
        return this;
    }

    public R success(Boolean success){
        this.success = success;
        return this;
    }

    public R message(String message){
        this.message = message;
        return this;
    }

    public R data(String key,Object value){
        this.data.put(key,value);
        return this;
    }

    public R data(Map<String,Object> map){
        this.data = map;
        return this;
    }
    

}

3、修改Controller中的返回结果

    @ApiOperation(value = "医院设置列表")
    @GetMapping("findAll")
    public R findAll(){
        List<HospitalSet> list = hospitalSetService.list();
        return R.ok().data("items",list);
    }

    @ApiOperation(value = "医院设置删除")
    @DeleteMapping("{id}")
    public R removeId(@ApiParam(name = "id",value = "医院编号",required = true) @PathVariable Integer id){
        hospitalSetService.removeById(id);
        return R.ok();
    }

八、分页和条件查询

1、HospPlusConfig中配置分页插件

放在不同模块下记得在启动类模块下添加@ComponentScan

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PageConfig {
    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

2、条件分页Controller方法

在这里插入图片描述
我们只需要两个字段医院名称和医院编号,所以我们使用VO类,而不是用POJO类
在这里插入图片描述

3、Controller方法

    @ApiOperation(value = "带查询的条件的分页")
    @PostMapping("/page/{pageNum}/{size}")
    public R getPageInfo(@ApiParam(name = "pageNum",value = "当前页") @PathVariable Integer pageNum,
                         @ApiParam(name = "size",value = "每页显示多少条数据") @PathVariable Integer size,
                         @RequestBody HospitalSetQueryVo hospitalSetQueryVo
                         ){
        Page<HospitalSet> page = new Page<>(pageNum, size);
        QueryWrapper<HospitalSet> queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(hospitalSetQueryVo.getHosname())){
            queryWrapper.like("hosname",hospitalSetQueryVo.getHosname());
        }
        if (!StringUtils.isEmpty(hospitalSetQueryVo.getHoscode())){
            queryWrapper.eq("hoscode",hospitalSetQueryVo.getHoscode());
        }
        hospitalSetService.page(page,queryWrapper);
        return R.ok().data("total",page.getTotal()).data("rows",page.getRecords());
    }

4、Swagger中测试

在这里插入图片描述

九、新增和修改医院设置工具

1、新增

    @ApiOperation(value = "新增接口")
    @PostMapping("save")
    public R save(@RequestBody HospitalSet hospitalSet){
        //设置状态 1 使用 0 不能使用
        hospitalSet.setStatus(1);
        //签名秘钥
        Random random = new Random();
        hospitalSet.setSignKey(MD5.encrypt(System.currentTimeMillis()+""+random.nextInt(1000)));
        
        hospitalSetService.save(hospitalSet);
        return R.ok();
    }

2、根据id查询

    //修改之回显数据
    @ApiOperation(value = "根据id查询医院设置")
    @GetMapping("/detail/{id}")
    public R detail(@PathVariable Integer id){
        return R.ok().data("items",hospitalSetService.getById(id));
    }

3、根据id修改

    //修改之修改数据
    @ApiOperation(value = "根据ID修改医院设置")
    @PutMapping("/update")
    public R update(@RequestBody HospitalSet hospitalSet){
        hospitalSetService.updateById(hospitalSet);
        return R.ok();
    }

十、批量删除医院设置和锁定医院设置接口

1、批量删除

   //批量删除医院设置
    @ApiOperation(value = "批量删除")
    @DeleteMapping("/delete")
    public R delete(@RequestBody List<Integer> ids){
        hospitalSetService.removeByIds(ids);
        return R.ok();
    }

2、锁定和解锁

   @ApiOperation(value = "锁定与解锁")
    @DeleteMapping("/status/{id}/{status}")
    public R status(@PathVariable Long id, @PathVariable Integer status){
        //HospitalSet byId = hospitalSetService.getById(id);  //乐观锁
        HospitalSet hospitalSet = new HospitalSet();
        hospitalSet.setId(id);
        hospitalSet.setStatus(status);
        hospitalSetService.updateById(hospitalSet);
        return R.ok();
    }

十一、统一异常处理

1、创建统一异常处理器

在service_utils中创建com.donglin.yygh.common.handler包,建统一异常处理类GlobalExceptionHandler.java:

import com.donglin.yygh.common.result.R;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

//凡是由@ControllerAdvice 标记的类都表示全局异常类
@RestControllerAdvice  //@ControllerAdvice+@RequestBody=@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)  //粒度有点大
    public R handleException(Exception ex){
        ex.printStackTrace();
        return R.error().message(ex.getMessage());
    }
}

JAVA异常getMessage(),printStackTrace()方法的区别https://donglin.blog.csdn.net/article/details/125471865

2、添加异常处理方法

GlobalExceptionHandler.java中添加

    @ExceptionHandler(value = SQLException.class)  
    public R handleException(SQLException ex){
        ex.printStackTrace();
        return R.error().message("Sql异常");
    }

    @ExceptionHandler(value = ArithmeticException.class)  
    public R handleException(ArithmeticException ex){
        ex.printStackTrace();
        return R.error().message("数学异常");
    }

3、测试

返回统一错误结果

4、自定义异常类

1、创建自定义异常类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class YyghException extends RuntimeException {

    @ApiModelProperty(value = "状态码")
    private Integer code;

    private String message;

}

2、业务中需要的位置抛出Exception

  try {
            int a = 10/0;
        }catch(Exception e) {
            throw new YyghException(40000,"xxx异常");
        }

3、添加异常处理方法
GlobalExceptionHandler.java中添加

   @ExceptionHandler(value = YyghException.class)
    public R handleException(YyghException ex){
        ex.printStackTrace();
        return R.error().message(ex.getMessage()).code(ex.getCode());
    }

在这里插入图片描述

十二、统一日志

1、配置日志级别

日志记录器(Logger)的行为是分等级的。如下表所示:
分为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL
默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别

# 设置日志级别
logging.level.root=WARN

这种方式只能将日志打印在控制台上

2、Logback日志

pring boot内部使用Logback作为日志实现的框架。
Logback和log4j非常相似,如果你对log4j很熟悉,那对logback很快就会得心应手。

3、配置logback日志

删除application.properties中的日志配置
安装idea彩色日志插件:grep-console
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="D:/hosptial project/yygh" />

    <!-- 彩色日志 -->
    <!-- 配置格式变量: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, ALLOFF,
              如果未设置此属性,那么当前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.donglin" level="INFO" />

        <!--
            root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
            level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALLOFF,默认是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>

4、将错误日志输出到文件

GlobalExceptionHandler.java 中
类上添加注解

@Slf4j

异常输出语句

 log.error(e.getMessage());

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/198087.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CDA Level Ⅱ 模拟题(二)

练习题 【单选题】1/20 一项针对全国25-35岁用户群的手机喜好调查&#xff0c;但调研项目经费大概是10万元&#xff0c;并且用户群相对集中在中国中部城市。前期预调研显示&#xff0c;用户群的数值方差和调研费用不等。以下哪种情况是比较适宜的调查方式&#xff1f; A.简单随…

【C++入门】

目录1、命名空间1.1、命名空间定义1.2、命名空间的使用2、C输入和输出3、缺省参数3.1 缺省参数概念3.2缺省参数分类4、函数重载4.1、函数重载概念4.2 C支持函数重载的原理--名字修饰5、引用5.1、引用概念5.2、引用特性5.3、常引用5.4、使用场景5.5、传值、传引用效率比较5.6、引…

【JavaEE】如何构造 HTTP请求认识HTTPS

✨哈喽&#xff0c;进来的小伙伴们&#xff0c;你们好耶&#xff01;✨ &#x1f6f0;️&#x1f6f0;️系列专栏:【JavaEE】 ✈️✈️本篇内容:如何构造 HTTP 请求同时认识HTTPS&#xff01; &#x1f680;&#x1f680;代码存放仓库gitee&#xff1a;JavaEE代码&#xff01; …

HW在即,那些被遗忘的物理安全还好吗?

近段时间&#xff0c;一个网络攻击的段子在互联网上火了起来。 “某公司被黑客勒索&#xff0c;每20分钟断一次网&#xff0c;给公司带来了极其严重的影响&#xff0c;但通过技术手段怎么也找不到问题。最后公司发现是黑客买通了保安&#xff0c;每20分钟拔一次网线。” 看完…

即时通讯系列---如何设计消息协议层方案

1. 前言 上篇即时通讯系列—如何下手做技术方案设计 最后总结出IM系统的端侧基本结构 后续文章将从下到上以此做架构设计. 本文Agenda 什么是消息同步同步协议的常见设计方案包含哪些应该采用哪种方案方案细节 2. 名词定义: 消息漫游 : 用户如何从消息服务器获取会话和消息…

MITK2021.02编译记录

编译成功效果 编译文件夹 参考教程 https://libaineu2004.blog.csdn.net/article/details/124202508?spm1001.2101.3001.6650.6&utm_mediumdistribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7ERate-6-124202508-blog-76460702.pc_relevant_aa2&depth_…

C语言——数据在内存中的存储

C语言——数据的存储一、C语言中常见的数据类型1.1 数据类型的基本归类二. 整型在内存中的存储2.1原码、反码、补码知识回顾2.2大小端字节序2.2.1 何为大小端字节序2.2.2为什么会产生大小端字节序2.2.3练习三、浮点型在内存中的存储3.1浮点数存储规则一、C语言中常见的数据类型…

Java两大工具库:Commons和Guava(5)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客。值此新春佳节&#xff0c;我给您拜年啦&#xff5e;祝您在新的一年中所求皆所愿&#xff0c;所行皆坦途&#xff0c;展宏“兔”&#xff0c;有钱“兔”&#xff0c;多喜乐&#xff0c;常安宁&#xff01;在开发中&#…

C语言最鸡肋的关键字

C语言的关键字有很多&#xff0c;要说最不常见的&#xff0c;我觉得应该是auto。 说它不常见&#xff0c;因为很多时候&#xff0c;我们都把它给省略了。 比如在函数内部定义变量a&#xff0c;正常人都会这样写&#xff1a; void function() {int a; }很少有人会在前面加一个…

1616_MIT 6.828 program header相关只是小结

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 前面刚刚分析了elf的header&#xff0c;看了两行代码又遇到了program的header。又是一个概念类的问题&#xff0c;还得去简单了解下。 1. 这里面的信息其实是可能有…

hgame2023 week2 writeup

WEEK2 文章目录WEEK2web1、Git Leakage2、v2board3、DesignerRE1、before_main2、stream3、VidarCamera4、mathpwn1、YukkuriSay2、editable_note3、fast_note4、new_fast_noteCrypto1、零元购年货商店2、包里有什么3、Rabin3、RSA 大冒险1misc1、Tetris Master2、Sign In Pro …

深入理解Linux网络学习总结

文章目录一、绪论二、内核如何接收网络包1、收包流程的一些核心概念2、网络接收过程总览三、内核如何与用户进程协作1、进程的一些基础概念3、同步阻塞IO工作流程4、epoll工作流程四、内核如何发送网络包1、发包流程的一些基础概念2、网络发送过程总览3、发包流程的内存拷贝操作…

Cnetsdk:PDF 转图像转换器 SDK 大文档轻松转

.NET PDF 到图像转换器 SDK&#xff0c;成熟的PDF转换器 CnetSDK .NET PDF Converter for Images是一个完整的PDF到图像转换解决方案。无需将任何其他 Adob​​e 或 PDF 阅读器安装到您的 .NET 应用程序&#xff08;C# 或 VB.NET&#xff09;&#xff0c;例如 Windows 和 ASP.N…

Cnetsdk.NET PDF 条码阅读器 SDK

.NET PDF 条码阅读器 SDK 易于整合 Cnetsdk.NET PDF 条码阅读器 SDK易于添加和集成到您的 C# 和 VB.NET 应用程序开发中。您只需要将您的 VS .NET 项目引用添加到 CnetSDK .NET PDF Barcode Reader SDK 库 dll。这个独立的 .NET 库与 .NET Framework 2.0、Visual Studio 2005、…

Linux(常用操作+服务器部署项目运行环境的过程记录)

Linux&#xff08;常用操作实例springboot项目&#xff08;包含数据库&#xff09;打包上传&#xff0c;配置到腾讯云轻量级服务器&#xff0c;中间一些错误也记录了解决方法和自己的理解&#xff09; Linux中一切皆文件【文件: 读写执行(查看、创建、删除、移动、复制、编辑)…

java易错题锦集一

易错题 字符串 题一 原题链接 public class Example { String str new String("good"); char[] ch {a,b,c}; public static void main(String[] args) { Example ex new Example(); ex.change(ex.str, ex.ch); System.out.print(ex.str "and"); Syst…

Spring设计模式总结

Spring 框架用到了哪些设计模式&#xff1f; 代理模式 总述 所谓代理&#xff0c;是指它与被代理对象实现了相同的接口&#xff0c;客户端必须通过代理才能与被代理的目标类进行交互&#xff0c;而代理一般在交互的过程中&#xff08;交互前后&#xff09;&#xff0c;进行某…

前端——周总结系列三

1 数组对象使用变量作为key加中括号 2 遍历数组异同 主要针对for…in&#xff0c; for…of &#xff0c;map遍历方法 for…in和for…of都可以遍历数组&#xff0c;其中for…of可以获取数组的每一项&#xff0c;for…in不仅可以获取每一项&#xff0c;也可以获取索引值 for…o…

加油站会员管理小程序实战开发教程06-地图功能开发

上一篇我们主要是讲解了前端搭建的方法,本篇我们介绍一下地图功能的开发。 在我们的原型里在首页需要显示当前加油站距你的距离。计算距离需要我们引入地图的API,在微搭中先需要创建API。 登录控制台,点击APIs,点击新建APIs 选择腾讯地图 这里需要输入API KEY,微搭已经给…

LeetCode 刷题系列 -- 124. 二叉树中的最大路径和

路径 被定义为一条从树中任意节点出发&#xff0c;沿父节点-子节点连接&#xff0c;达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。路径和 是路径中各节点值的总和。给你一个二叉树的根节点 root &…