ApiBoot Logging
能够无缝整合SpringCloud
来采集请求日志,目前支持RestTemplate
、Openfeign
两种方式,咱们本章来说解下在使用Openfeign
完成服务之间请求相互调用的一条链路请求日志是否能够都采集到。html
博客原文地址:blog.yuqiyu.com/apiboot-log…java
咱们先来搭建一个Eureka Server
,请访问【搭建服务注册中心Eureka Server】文章内容查看具体搭建流程。git
咱们须要搭建一个Logging Admin
用于接收Logging Client
上报的请求日志,请访问【ApiBoot Logging整合SpringCloud Eureka负载均衡上报日志】查看具体的搭建流程。web
咱们本章来模拟提交订单的业务逻辑,涉及到两个服务,分别是:商品服务
、订单服务
,接下来咱们须要来建立这两个服务。spring
本章源码采用Maven
多模块的形式进行编写,请拉至文章末尾查看下载本章源码。json
因为是采用Maven 多模块
项目,存在继承关系,咱们只须要在root
模块添加版本依赖便可,其余子模块就能够直接使用,以下所示:api
<properties>
<java.version>1.8</java.version>
<!--ApiBoot版本号-->
<api.boot.version>2.1.5.RELEASE</api.boot.version>
<!--SpringCloud版本号-->
<spring.cloud.version>Greenwich.SR3</spring.cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-dependencies</artifactId>
<version>${api.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
复制代码
学习过Openfeign
的同窗应该都知道,Openfeign
能够继承实现,咱们只须要建立一个公共的服务接口定义,在实现该接口的服务进行业务实现,在调用该接口的地方直接注入便可。 下面咱们建立一个名为common-openfeign
的公共依赖项目,pom.xml
添加依赖以下所示:bash
<dependencies>
<!--SpringBoot Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<!--Openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
复制代码
在提交订单时咱们简单模拟须要获取商品的单价,因此在common-openfeign
项目内咱们要提供一个查询商品单价的服务接口,建立一个名为GoodClient
的接口以下所示:架构
package org.minbox.chapter.common.openfeign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/** * 商品服务接口定义 * * @author 恒宇少年 */
@FeignClient(name = "good-service")
@RequestMapping(value = "/good")
public interface GoodClient {
/** * 获取商品价格 * * @param goodId 商品编号 * @return */
@GetMapping(value = "/{goodId}")
Double getGoodPrice(@PathVariable("goodId") Integer goodId);
}
复制代码
注解解释:app
@FeignClient
:SpringCloud Openfeign
提供的接口客户端定义注解,经过value
或者name
来指定GoodClient
访问的具体ServiceID
,这里咱们配置的value
值为good-service
项目spring.application.name
配置参数(ServiceID
= spring.application.name
)。这样当咱们经过注入
GoodClient
接口调用getGoodPrice
方法时,底层经过Openfeign
的Http
代理访问good-service
的对应接口。
下面咱们再来建立一个名为good-service
的SpringBoot
项目。
在pom.xml
项目配置文件内添加以下依赖:
<dependencies>
<!--ApiBoot Logging Client-->
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-logging</artifactId>
</dependency>
<!--SpringBoot Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--公共Openfeign接口定义依赖-->
<dependency>
<groupId>org.minbox.chapter</groupId>
<artifactId>common-openfeign</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
复制代码
能够看到咱们在good-service
项目依赖内添加了咱们在上面建立的common-openfeign
依赖模块,由于GoodClient
服务接口的实现是在good-service
项目内,咱们须要添加common-openfeign
依赖后建立对应的XxxController
并实现GoodClient
接口完成对应的业务逻辑实现。
这里咱们简单作个示例,将价格固定返回,实现GoodClient
的控制器以下所示:
package org.minbox.chapter.good.service;
import org.minbox.chapter.common.openfeign.GoodClient;
import org.springframework.web.bind.annotation.RestController;
/** * 商品服务接口实现 * * @author 恒宇少年 * @see GoodClient */
@RestController
public class GoodController implements GoodClient {
@Override
public Double getGoodPrice(Integer goodId) {
if (goodId == 1) {
return 15.6;
}
return 0D;
}
}
复制代码
咱们须要将good-service
注册到Eureka Server
,修改application.yml
配置文件以下所示:
# ServiceID
spring:
application:
name: good-service
# 端口号
server:
port: 8082
# Eureka Config
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10000/eureka/
instance:
prefer-ip-address: true
复制代码
咱们须要将good-service
的请求日志上报到Logging Admin
,采用SpringCloud ServiceID
的方式配置,修改application.yml
配置文件以下所示:
api:
boot:
logging:
# 控制台打印日志
show-console-log: true
# 美化打印日志
format-console-log-json: true
# 配置Logging Admin 服务编号
discovery:
service-id: logging-admin
复制代码
最后咱们在XxxApplication
入口类添加注解来启用Eureka Client
以及Logging Client
,以下所示:
/** * 商品服务 * * @author 恒宇少年 */
@SpringBootApplication
@EnableLoggingClient
@EnableDiscoveryClient
public class GoodServiceApplication {
/** * logger instance */
static Logger logger = LoggerFactory.getLogger(GoodServiceApplication.class);
public static void main(String[] args) {
SpringApplication.run(GoodServiceApplication.class, args);
logger.info("{}服务启动成功.", "商品");
}
}
复制代码
至此咱们的商品服务已经准备完成.
建立一个名为order-service
的SpringBoot
项目(建议参考源码,本章采用Maven多模块建立)。
修改pom.xml
添加相关依赖以下所示:
<dependencies>
<!--ApiBoot Logging Client-->
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-logging</artifactId>
</dependency>
<!--SpringBoot Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Eureka Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--Openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--公共Openfeign接口定义依赖-->
<dependency>
<groupId>org.minbox.chapter</groupId>
<artifactId>common-openfeign</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
复制代码
咱们来模拟一个提交订单的场景,建立一个名为OrderController
的控制器,以下所示:
/** * 订单控制器 * * @author 恒宇少年 */
@RestController
@RequestMapping(value = "/order")
public class OrderController {
/** * 商品接口定义注入 * {@link GoodClient} */
@Autowired
private GoodClient goodClient;
@PostMapping
public String submit(Integer goodId, Integer buyCount) {
Double goodPrice = goodClient.getGoodPrice(goodId);
Double orderAmount = goodPrice * buyCount;
//...
return "订单提交成功,订单总金额:" + orderAmount;
}
}
复制代码
将咱们建立的order-service
注册到Eureka Server
,修改application.yml
配置文件以下所示:
spring:
application:
name: order-service
server:
port: 8081
# Eureka Config
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10000/eureka/
instance:
prefer-ip-address: true
复制代码
咱们须要将order-service
的请求日志上报到Logging Admin
,采用SpringCloud ServiceID
的方式配置,修改application.yml
配置文件以下所示:
api:
boot:
logging:
# 控制台打印日志
show-console-log: true
# 美化打印日志
format-console-log-json: true
# 配置Logging Admin 服务编号
discovery:
service-id: logging-admin
复制代码
修改order-service
入口类OrderServiceApplication
,添加启用Eureka Client
、Logging Client
的注解,以下所示:
/** * 订单服务 * * @author 恒宇少年 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableLoggingClient
@EnableFeignClients(basePackages = "org.minbox.chapter.common.openfeign")
public class OrderServiceApplication {
/** * logger instance */
static Logger logger = LoggerFactory.getLogger(OrderServiceApplication.class);
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
logger.info("{}服务启动成功.", "");
}
}
复制代码
注解解释:
@EnableFeignClients
:该注解是Openfeign
提供的启用自动扫描Client
的配置,咱们经过basePackages
(基础包名)的方式进行配置扫描包下配置@FeignClient
注解的接口,并为每一个接口生成对应的代理实现
并添加到Spring IOC
容器。
org.minbox.chapter.common.openfeign
包名在common-openfeign
项目内。
依次启动项目,eureka-server
> logging-admin
> good-service
> order-service
。
经过curl
命令访问order-service
内的提交订单地址:/order
,以下所示:
➜ ~ curl -X POST http://localhost:8081/order\?goodId\=1\&buyCount\=3
订单提交成功,订单总金额:46.8
复制代码
能够看到咱们已经能够成功的获取订单的总金额,咱们在
/order
请求方法内调用good-service
获取商品的单价后计算获得订单总金额。
咱们经过控制台输出的日志信息来确认下链路信息(traceId、spanId)的透传是否正确。
收到order-service上报的日志
Receiving Service: 【order-service -> 127.0.0.1】, Request Log Report,Logging Content:[
{
"endTime":1573009439840,
"httpStatus":200,
"requestBody":"",
"requestHeaders":{
"host":"localhost:8081",
"user-agent":"curl/7.64.1",
"accept":"*/*"
},
"requestIp":"0:0:0:0:0:0:0:1",
"requestMethod":"POST",
"requestParam":"{\"buyCount\":\"3\",\"goodId\":\"1\"}",
"requestUri":"/order",
"responseBody":"订单提交成功,订单总金额:46.8",
"responseHeaders":{},
"serviceId":"order-service",
"serviceIp":"127.0.0.1",
"servicePort":"8081",
"spanId":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
"startTime":1573009439301,
"timeConsuming":539,
"traceId":"3e20cc72-c880-4575-90ed-d54a6b4fe555"
}
]
复制代码
收到good-service上报的日志
Receiving Service: 【good-service -> 127.0.0.1】, Request Log Report,Logging Content:[
{
"endTime":1573009439810,
"httpStatus":200,
"parentSpanId":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
"requestBody":"",
"requestHeaders":{
"minbox-logging-x-parent-span-id":"241ef717-b0b3-4fcc-adae-b63ffd3dbbe4",
"minbox-logging-x-trace-id":"3e20cc72-c880-4575-90ed-d54a6b4fe555",
"host":"10.180.98.156:8082",
"connection":"keep-alive",
"accept":"*/*",
"user-agent":"Java/1.8.0_211"
},
"requestIp":"10.180.98.156",
"requestMethod":"GET",
"requestParam":"{}",
"requestUri":"/good/1",
"responseBody":"15.6",
"responseHeaders":{},
"serviceId":"good-service",
"serviceIp":"127.0.0.1",
"servicePort":"8082",
"spanId":"6339664e-097d-4a01-a734-935de52a7d44",
"startTime":1573009439787,
"timeConsuming":23,
"traceId":"3e20cc72-c880-4575-90ed-d54a6b4fe555"
}
]
复制代码
结果分析:
请求日志的入口为order-service
因此并不存在parentSpanId
(上级单元编号),而spanId
(单元编号)、traceId
(链路编号)也是新生成的。
本次请求会通过good-service
服务,所以parentSpanId
则是order-service
生成的spanId
,traceId
一样也是order-service
生成的,透传HttpHeader方式进行传递,表示在同一条请求链路上。
ApiBoot Logging
支持使用Openfeign
传递链路信息,内部经过Openfeign
拦截器实现,源码详见:org.minbox.framework.logging.client.http.openfeign.LoggingOpenFeignInterceptor
。
将traceId
(链路编号)、parentSpanId
(单元编号)经过HttpHeader
的形式传递到目标访问服务,服务经过请求日志拦截器进行提取并设置链路绑定关系。
traceId
传递时HttpHeader名称为:minbox-logging-x-trace-id
.parentSpanId
传递时HttpHeader名称为:minbox-logging-x-parent-span-id
若是您喜欢本篇文章请为源码仓库点个Star
,谢谢!!! 本篇文章示例源码能够经过如下途径获取,目录为SpringBoot2.x/apiboot-logging-using-openfeign-transparent-traceid
: