首页 > 其他分享 >Swagger和日志

Swagger和日志

时间:2023-07-20 09:12:41浏览次数:26  
标签:logging private ApiModelProperty 接口 套餐 日志 Swagger

Swagger

官网:https://swagger.io/

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

功能 主要包含以下几点:

A. 使得前后端分离开发更加方便,有利于团队协作

B. 接口文档在线自动生成,降低后端开发人员编写接口文档的负担

C. 接口功能测试

直接使用Swagger, 需要按照Swagger的规范定义接口, 实际上就是编写Json文件,编写起来比较繁琐、 并不方便, 。而在项目中使用,我们一般会选择一些现成的框架来简化文档的编写,而这些框架是基于 Swagger的,如knife4j。knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。而我们 要使用kinfe4j,需要在pom.xml中引入如下依赖即可:

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

knife4j是swagger的增强版

使用方式

使用knife4j,主要需要操作以下几步:

1). 导入knife4j的maven坐标

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

2). 导入knife4j相关配置类

创建一个配置类(也就是加了@Configuration的注解)

A. 在该配置类中加上两个注解 @EnableSwagger2 @EnableKnife4j ,开启Swagger和Knife4j的功能。

B. 在配置类中声明一个Docket类型的bean, 通过该bean来指定生成文档的相关信息。

@Configuration
@EnableSwagger2
@EnableKnife4j
public class MyKnife4jConfiguration {
@Bean
public Docket createRestApi() {
// 文档类型
     return new Docket(DocumentationType.SWAGGER_2)
          .apiInfo(apiInfo()) // 我们生成文档的一些配置 包含标题 版本号 描述
          .select()
          .apis(RequestHandlerSelectors.basePackage("com.yxh.reggie.controller")) // 写我们controller所在的包 因为我们就是给controller中的方法来生成接口文档
          .paths(PathSelectors.any())
          .build();
}
private ApiInfo apiInfo() {
     return new ApiInfoBuilder()
          .title("体检项目")
          .version("1.0")
          .description("体检项目接口文档")
          .build();
     }
}

注意: Docket声明时,指定的有一个包扫描的路径,该路径指定的是Controller所在包的路径。 因为Swagger在生成接口文档时,就是根据这里指定的包路径,自动的扫描该包下的 @Controller, @RestController, @RequestMapping等SpringMVC的注解,依据这些注解来生 成对应的接口文档。

查看接口文档

经过上面的集成配置之后,我们的项目集成Swagger及Knife4j就已经完成了,接下来我们可以重新启动 项目,访问接口文档,访问链接为: http://localhost:8080/doc.html

常用注解

为了解决上述的问题,Swagger提供了很多的注解,通过这些注解,我们可以更好更清晰的描述我们的 接口,包含接口的请求参数、响应数据、数据模型等。核心的注解,主要包含以下几个:

注解 位置 说明
@Api 加载Controller类上,表示对类的说明
@ApiModel 类(通常是实 体类) 描述实体类的作用
@ApiModelProperty 属性 描述实体类的属性
@ApiOperation 方法 说明方法的用途、作用
@ApiImplicitParams 方法 表示一组参数说明
@ApiImplicitParam 方法 用在@ApiImplicitParams注解中,指定一个请求参数的 各个方面的属性

注解测试

可以通过 @ApiModel , @ApiModelProperty 来描述实体类及属性

/**
* 套餐
*/
@Data
@ApiModel("套餐")
public class Setmeal implements Serializable {
     private static final long serialVersionUID = 1L;
     @ApiModelProperty("主键")
     private Long id;
     //分类id
     @ApiModelProperty("分类id")
     private Long categoryId;
     //套餐名称
     @ApiModelProperty("类别名字")
     private String name;
     / //套餐价格
     @ApiModelProperty("套餐价格")
     private BigDecimal price;
     //状态 0:停用 1:启用
     @ApiModelProperty("状态")
     private Integer status;
     //编码
     @ApiModelProperty("套餐编号")
     private String code;
     //描述信息
     @ApiModelProperty("描述信息")
     private String description;
     //图片
     @ApiModelProperty("图片")
     private String image;
     @ApiModelProperty("创建时间")
     private LocalDateTime createTime;
     @ApiModelProperty("更新时间")
     private LocalDateTime updateTime;
     @ApiModelProperty("创建人")
     private Long createUser;
     @ApiModelProperty("更新者")
     private Long updateUser;
}

响应实体R

@Data
@ApiModel("返回结果")
public class R<T> implements Serializable{
	@ApiModelProperty("编码")
	private Integer code; //编码:1成功,0和其它数字为失败
	@ApiModelProperty("错误信息")
	private String msg; //错误信息
	@ApiModelProperty("数据")
	private T data; //数据
	@ApiModelProperty("动态数据")
	private Map map = new HashMap(); //动态数据
	//省略静态方法 ....
}

Controller类及其中的方法

描述Controller、方法及其方法参数,可以通过注解: @Api, @APIOperation, @ApiImplicitParams, @ApiImplicitParam

@RestController
@RequestMapping("/setmeal")
@Slf4j
@Api(tags = "套餐相关接口")
public class SetmealController {
	@Autowired
	private SetmealService setmealService;
	@Autowired
	private CategoryService categoryService;
	@Autowired
	private SetmealDishService setmealDishService;
	/**
	* 新增套餐
	* @param setmealDto
	* @return
	*/
	@PostMapping
	@CacheEvict(value = "setmealCache",allEntries = true)
	@ApiOperation(value = "新增套餐接口")
	public R<String> save(@RequestBody SetmealDto setmealDto){
		log.info("套餐信息:{}",setmealDto);
		setmealService.saveWithDish(setmealDto);
		return R.success("新增套餐成功");
	}
	/**
	* 套餐分页查询
	* @param page
	* @param pageSize
	* @param name
	* @return
	*/
	@GetMapping("/page")
	@ApiOperation(value = "套餐分页查询接口")
	@ApiImplicitParams({
	@ApiImplicitParam(name = "page",value = "页码",required = true),
	@ApiImplicitParam(name = "pageSize",value = "每页记录数",required =
	true),
	@ApiImplicitParam(name = "name",value = "套餐名称",required = false)
	})
	public R<Page> page(int page,int pageSize,String name){
		//分页构造器对象
		Page<Setmeal> pageInfo = new Page<>(page,pageSize);
		Page<SetmealDto> dtoPage = new Page<>();
		LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
		//添加查询条件,根据name进行like模糊查询
		queryWrapper.like(name != null,Setmeal::getName,name);
		//添加排序条件,根据更新时间降序排列
		queryWrapper.orderByDesc(Setmeal::getUpdateTime);
		setmealService.page(pageInfo,queryWrapper);
		//对象拷贝
		BeanUtils.copyProperties(pageInfo,dtoPage,"records");
		List<Setmeal> records = pageInfo.getRecords();
		List<SetmealDto> list = records.stream().map((item) -> {
			SetmealDto setmealDto = new SetmealDto();
			//对象拷贝
			BeanUtils.copyProperties(item,setmealDto);
			//分类id
			Long categoryId = item.getCategoryId();
			//根据分类id查询分类对象
			Category category = categoryService.getById(categoryId);
			if(category != null){
				//分类名称
				String categoryName = category.getName();
				setmealDto.setCategoryName(categoryName);
			}
			return setmealDto;
		}).collect(Collectors.toList());
		dtoPage.setRecords(list);
		return R.success(dtoPage);
	}
/**
* 删除套餐
* @param ids
* @return
*/
@DeleteMapping
@CacheEvict(value = "setmealCache",allEntries = true)
@ApiOperation(value = "套餐删除接口")
public R<String> delete(@RequestParam List<Long> ids){
	log.info("ids:{}",ids);
	setmealService.removeWithDish(ids);
	return R.success("套餐数据删除成功");
}
/**
* 根据条件查询套餐数据
* @param setmeal
* @return
*/
@GetMapping("/list")
@Cacheable(value = "setmealCache",key = "#setmeal.categoryId + '_' +#setmeal.status")
@ApiOperation(value = "套餐条件查询接口")
public R<List<Setmeal>> list(Setmeal setmeal){
		LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(setmeal.getCategoryId() !=null,Setmeal::getCategoryId,setmeal.getCategoryId());
		queryWrapper.eq(setmeal.getStatus() !=null,Setmeal::getStatus,setmeal.getStatus());
		queryWrapper.orderByDesc(Setmeal::getUpdateTime);
		List<Setmeal> list = setmealService.list(queryWrapper);
		return R.success(list);
	}
}
           
@RequestMapping("/add")
@ApiOperation("新增用户")
@ApiImplicitParams({
	@ApiImplicitParam(name = "User")
})
public String add(@RequestBody User user){
	return "add";
}

重启服务测试

日志

什么是日志

日志:就是介绍一个过程和经历的详细记录。

项目日志:就是项目开发过程的详细记录,一般由项目经理记录。

代码里的日志:就是程序员记录某个开发过程的详细情况,这是项目里每个程序员需要做的工作。 日志和异常处理结合得当的话,会给项目维护带来非常大的价值。

日志的重要性

日志,通常不会在需求阶段作为一个功能单独提出来,也不会在产品方案中看到它的细节。但是,这丝 毫不影响它在任何一个系统中的重要地位。

为了保证服务的高可用,发现问题一定要及时,解决问题一定要迅速,所以生产环境一旦出现问题,预 警系统就会通过邮件、短信甚至电话的方式实施多维轰炸模式,确保相关负责人不错过每一个可能的 bug。预警系统判断疑似 bug 大部分源于日志。当该错误日志达到一定次数出现的时候,就会触发报 警。

日志在项目中的作用

主要用于记录程序运行的情况,以便于程序在部署之后的排错调试等,也有利于将这些信息进行持久化 (如果不将日志信息保存到文件或数据库,则信息便会丢失)。

查看程序当前运行状态

查看程序历史运行轨迹

排查系统问题

优化系统性能

安全审计的基石

Java 日志使用的困惑

作为 Java 程序员,幸运的是,Java 拥有功能和性能都非常强大的日志库;不幸的是,日志库 不止一个,JUL(java.util.logging), JCL( Jakarta Commons Logging,Spring 框架默认使用的), Log4j, Log4j2, Logback、 SLF4j、 jboss-logging(Hibernate 框架默认使用的) 等,这么多的日志 工具到底使用什么感到困惑。 使用困惑:有的程序员即使知道写 Java 程序用什么日志工具,可能对日志记录具体应该怎么写,写什么 东西,什么情况下要写,这些仁者见仁智者见智的东西也会产生困惑。

Java 日志演化历史

最先出现的是 Apache 开源社区的 Log4j,这个日志确实是应用最广泛的日志工具,成为了 Java 日志的 事实上的标准。

然而,当时 Java 的开发主体 Sun 公司认为自己才是正统,在 Jdk1.4 中增加了 JUL 日志实现,企图对抗 Log4j,但是却造成了 Java 目前开发者记录日志局面的混乱,迄今为止仍饱受诟病。 想象下你的项目应用使用 Log4j,然后使用了一个第三方库,而第三方库使用了 JUL,那么,你的应用就 得同时用 Log4j 和 JUL 两个日志工具了,然后又有需要使用另外一个第三方库,但是这个第三方库使用 了 Log4j 和 JUL 之外的simplelog。这个时候你的应用里各种log工具满天飞,这势必会使你的程序员感 到崩溃。因为这些日志工具互相没有关联,替换和统一日志工具也就变成了比较 棘手的一件事情。 为了搞定这个日常开发中比较棘手的问题,Apache 提供了一个日志框架作为日志的抽象,名字为 JCL。 JCL 对各种日志接口进行抽象,

抽象出一个接口层,对每个日志实现都进行适配,这样这些提供给别人的库都直接使用抽象层即可,确 实出色地完成了兼容主流的日志实现(Log4j、JUL、simplelog 等),较好的解决了上述问题,基本一 统江湖,就连顶顶大名的 Spring 也是依赖了 JCL。

但是美好的日子并不长,作为元老级日志 Log4j 的作者 (Ceki Gülcü),他觉得JCL 不够优秀,所以他再度 出山,搞出了一套更优雅的日志框架 SLF4J(这个也是抽象层),即简单日志门面(Simple Logging Facade for Java),并为 SLF4J实现了一个亲儿子——logback, 确实更加优雅了。

最后,Ceki Gülcü 觉得还是得照顾下自己的 “大儿子”——Log4j,又把 Log4j进行了改造,就是所谓的 Log4j2,同时支持 JCL 以及SLF4J。 SLF4J 的出现,又使 Java 日志体系变得混乱起来。

下面是一张目前 Java 日志体系的示意图

image-20230717142411334

image-20230717142418770

java common logging 和 SLF4J 都是日志的接口,供用户使用,而没有提供实现,Log4j,JUL, logback 等等才是日志的真正实现。

Logback和log4j2是同一家公司的

通常情况下,日志是由一个抽象层+实现层的组合来搭建的。 Spring Boot 的日志框架:默认情况下,Spring Boot 会用 Logback 来记录日 志,并用 INFO 级别输出 到控制台

Logback 的介绍

Logback 是 log4j 框架的作者开发的新一代日志框架,它效率更高、能够适应诸多的运行环境,同时天 然支持 SLF4J。

官方网站: http://logback.qos.ch

它当前分为下面下个模块:

logback-core:其它两个模块的基础模块

logback-classic:它是 log4j 的一个改良版本,同时它完整实现了 slf4j API 使你可以很方便地更换成其 它日志系统如 log4j 或 JDK1.4

Logging logback-access:与 Servlet 容器集成提供通过 Http 来访问日志的功能

Spring Boot 中整合 Logback

Spring Boot 为我们提供了很多默认的日志配置,只要将 spring-boot-starter-logging 作为依赖加入到 当前应用的 classpath,则“开 箱即用”。 添加日志依赖(默认已经添加了,不需要添加)

org.springframework.boot

spring-boot-starter-logging

image-20230717142624391

默认的输出格式

日志的输出项目

日期和时间:毫秒精度且易于排序。

日志级别:ERROR,WARN,INFO,DEBUG,或 TRACE。 进程标识。

一个—分离器来区分实际日志消息的开始。

线程名称:括在方括号中(可能会被截断以用于控制台输出)。

记录器名称:这通常是源类名称(通常缩写)。

日志消息。

彩色编码输出 如果您的终端支持 ANSI,则使用颜色输出来提高可读性。

下表描述了日志级别到颜色的映射

image-20230717142803103

支持的颜色:blue、cyan、faint、green、magenta、red、yellow

日志级别

日志级别从低到高分为 TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为 WARN,则低 于 WARN 的信息都不会输出。

在 Spring 中设置日志级别,方法是使用 logging.level.=。 文件输出

默认情况下,Spring Boot 只记录到控制台,不写入日志文件。如果你想写入日志文件,你需要设置 logging.file.name 或 logging.file.path 属性

属性设置

logging.file:设置日志文件,可以是绝对路径,也可以是相对路径。如:

logging.file=my.log。

logging.path:设置目录,会在该目录下创建 spring.log 文件,并写入日志内容,如:

logging.path=D:/var/log。

注:二者不能同时使用,如若同时使用,则只有 logging.file 生效

image-20230717143010320

日志文件在达到 10 MB 时会旋转,并且与控制台输出一样,默认情况下会 记录 ERROR-level、WARNlevel 和 INFO-level 消息。可以使用该 logging.file.max-size 属性更改大小限制。除非 logging.file.maxhistory 已设置该属性,否则默认情况下会保留最近 7 天的轮换日志

文件。日志档案 的总大小可以使用 logging.file.total-size-cap. 当日志档案的总大小超过 该阈值时,将 删除备份。要在应用程序启动时强制清除日志存档,请使用该 logging.file.clean-history-on-start 属 性。

2.8程序中输出自己的日志

使用@Slf4j 注解

1. 实现案例
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class TestController {
 	@GetMapping("/")
 	public String index(){
 		log.info("记录日志使用 lombok 插件提供注解@Slf4j");
 		return "";
 	}
 }

标签:logging,private,ApiModelProperty,接口,套餐,日志,Swagger
From: https://www.cnblogs.com/YxinHaaa/p/17567350.html

相关文章

  • oracle用户密码过期导致归档日志文件无法删除
    问题描述:oracle用户密码过期导致归档日志文件无法删除.系统:rhel7.3场景描述:生产环境系统u01目录使用率超告警阀值,经确认为归档占用大量空间,系统中有自动清理归档日志文件的脚本,为什么没有清理呢?1、异常分析--查/var/log/cron日志文件异常告警信息:Jul1001:00:01hisdb01CR......
  • springboot日志
    日志简述 我们具体应该怎么选用  如何使用 首先应该导入抽象框架,搞出日志记录器,穿进去你想记录的类日志适配层 最上都是抽象接口,中间是适配层,来适配本来不也是slf4j框架的包装层解决历史遗留问题 如何把其他框架的日志替换成slf4j,把原来包里的替换成ov......
  • Python日志模块:实战应用与最佳实践
    本文详细解析了Python的logging模块,从基本介绍到实际应用和最佳实践。我们通过具体的代码示例解释了如何高效地使用这个模块进行日志记录,以及如何避免常见的陷阱,旨在帮助读者更好地掌握这个强大的工具。一、Python日志模块简介日志的概念及其在软件开发中的作用在开发过程中,......
  • .net 6 winform启动器:调用CMD命令行执行dotnet命令启动.net core程序并将控制台输出日
    背景历史遗留问题,生产车间运行的一个.netcoresignalr程序使用命令行程序启动,经常由于生产人员误操作将光标停留在控制台上导致程序假死,丢失部分测试数据,车间随便找了一台win10系统部署的程序,源代码遗失,电脑也是阉割版,原开发者已经离职多年无人敢动,于是想了一个歪门邪道发方......
  • GreatSQL通过错误日志信息判断数据库实例是如何关闭的
    背景概述在一次客户的数据库实例连接不上了,需要我们排查一下原因,通过查看数据库实例进程已经不存在了,在错误日志中没有发现其他报错信息,发现有shutdown的字样出现,怀疑是某个用户手动关闭了实例。我们通过以下测试,发现是由于用户关闭了主机所导致的。问题复现本次测试基于GreatS......
  • 2023-07-19 记录swagger接口文档如何实现复制api功能【转载】
    快捷入口:https://www.cnblogs.com/shanfeng1000/p/16285715.html说明:后端小伙伴提供的swagger接口文档给前端使用,前端发现比较难复制接口文档的api地址,故作为前端的我,给后端整活了,弄了一个解决方案,链接在上方......
  • springBoot 2.7.x整合 swagger2.9
    1.添加依赖<!--swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dep......
  • python日志记录代码
    Python日志记录的实现步骤为了帮助这位刚入行的小白实现Python日志记录代码,我们将按照以下步骤进行操作。这些步骤将指导他从头开始创建一个基本的日志记录系统。我们首先给出这些步骤的概述,然后逐一介绍每一个步骤的细节以及相应的代码。步骤概述步骤描述1导入loggin......
  • go语言读取docker容器的日志
    使用DockerSDK:要使用DockerSDK来读取Docker容器的日志,您需要安装DockerSDK并使用其提供的函数进行操作。安装DockerSDK:goget-ugithub.com/docker/docker获取容器ID#短IDdockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAM......
  • shell脚本-Nginx访问日志分析
    shell脚本-Nginx访问日志分析1.原理可以通过/usr/local/nginx/logs/access.log文件-查看nginx的日志[root@localhostscripts]#tail-f/usr/local/nginx/logs/access.log192.168.70.1-[17/Jul/2023:17:35:21+0800]"GET/HTTP/1.1"200173833"-""Mozilla......