ApiBoot Logging
能夠無縫整合SpringCloud
來採集請求日誌,目前支持RestTemplate
、Openfeign
兩種方式,咱們本章來說解下在使用Openfeign
完成服務之間請求相互調用的一條鏈路請求日誌是否能夠都採集到。
博客原文地址:http://blog.yuqiyu.com/apiboot-logging-using-openfeign-transparent-traceid.htmlhtml
咱們先來搭建一個Eureka Server
,請訪問【搭建服務註冊中心Eureka Server】文章內容查看具體搭建流程。java
咱們須要搭建一個Logging Admin
用於接收Logging Client
上報的請求日誌,請訪問【ApiBoot Logging整合SpringCloud Eureka負載均衡上報日誌】查看具體的搭建流程。git
咱們本章來模擬提交訂單的業務邏輯,涉及到兩個服務,分別是:商品服務
、訂單服務
,接下來咱們須要來建立這兩個服務。web
本章源碼採用Maven
多模塊的形式進行編寫,請拉至文章末尾查看下載本章源碼。spring
因爲是採用Maven 多模塊
項目,存在繼承關係,咱們只須要在root
模塊添加版本依賴便可,其餘子模塊就能夠直接使用,以下所示:json
<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
添加依賴以下所示:api
<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
的接口以下所示:bash
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); }
註解解釋:架構
@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
項目。app
在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
:
做者我的 博客
使用開源框架 ApiBoot 助你成爲Api接口服務架構師