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
: