ApiBoot Logging can seamlessly integrate spring cloud to collect request logs. Currently, it supports RestTemplate and Openfeign. In this chapter, we will explain whether the request logs of a link that requests mutual calls between services can be collected by using Openfeign.
Original blog address: http://blog.yuqiyu.com/apiboot-logging-using-openfeign-transparent-traceid.html
Building Eureka Server
Let's build an Eureka Server first. Please visit[ Set up Eureka Server as service registration center ]See the specific construction process for article content.
Setting up Logging Admin
We need to build a Logging Admin to receive the request log reported by the Logging Client. Please visit[ ApiBoot Logging integrates spring cloud Eureka load balancing reporting logs ]View the specific construction process.
In this chapter, we will simulate the business logic of order submission, involving two services: commodity service and order service. Next, we need to create these two services.
The source code of this chapter is written in the form of Maven multi module. Please check and download the source code of this chapter at the end of the article.
Add apiboot & spring cloud unified version
Because Maven multi module project is adopted and there is inheritance relationship, we only need to add version dependency in root module, and other sub modules can be used directly, as shown below:
<properties> <java.version>1.8</java.version> <!--ApiBoot Version number--> <api.boot.version>2.1.5.RELEASE</api.boot.version> <!--SpringCloud Version number--> <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>
Create public Openfeign interface definition
Students who have studied Openfeign should know that Openfeign can inherit the implementation. We only need to create a public service interface definition, implement the service of the interface, and inject directly where the interface is called. Next, we create a public dependency project named common openfeign. The dependency added to pom.xml is as follows:
<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>
When submitting an order, we simply simulate the need to obtain the unit price of the goods, so in the common openfeign project, we need to provide a service interface to query the unit price of the goods, and create an interface named GoodClient as follows:
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; /** * Definition of commodity service interface * * @author Heng Yu junior */ @FeignClient(name = "good-service") @RequestMapping(value = "/good") public interface GoodClient { /** * Get commodity price * * @param goodId Commodity number * @return */ @GetMapping(value = "/{goodId}") Double getGoodPrice(@PathVariable("goodId") Integer goodId); }
Note explanation:
- @FeignClient: the interface client definition annotation provided by spring cloud openfeign. The specific ServiceID accessed by GoodClient is specified by value or name. Here, the value value we configured is the configuration parameter of spring.application.name (ServiceID = spring.application.name) of the good service project.
In this way, when we call the getGoodPrice method by injecting the GoodClient interface, the underlying layer accesses the corresponding interface of the good service through the Http proxy of Openfeign.
Create goods and services
Let's create a spring boot project called good service.
Add dependency
Add the following dependencies to the pom.xml project configuration file:
<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> <!--public Openfeign Interface definition dependency--> <dependency> <groupId>org.minbox.chapter</groupId> <artifactId>common-openfeign</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies>
We can see that we have added the common openfeign dependency module we created above in the good service project dependency. Because the implementation of the GoodClient service interface is in the good service project, we need to add the common openfeign dependency to create the corresponding XxxController and implement the GoodClient interface to complete the corresponding business logic implementation.
Commodity business realization
Here we simply do an example to fix the price and return it. The controller to implement GoodClient is as follows:
package org.minbox.chapter.good.service; import org.minbox.chapter.common.openfeign.GoodClient; import org.springframework.web.bind.annotation.RestController; /** * Commodity service interface implementation * * @author Heng Yu junior * @see GoodClient */ @RestController public class GoodController implements GoodClient { @Override public Double getGoodPrice(Integer goodId) { if (goodId == 1) { return 15.6; } return 0D; } }
Register to Eureka Server
We need to register the good service with Eureka Server and modify the application.yml configuration file as follows:
# ServiceID spring: application: name: good-service # Port number server: port: 8082 # Eureka Config eureka: client: service-url: defaultZone: http://127.0.0.1:10000/eureka/ instance: prefer-ip-address: true
Configuring Logging Admin for escalation
We need to report the request log of good service to Logging Admin and configure it in the way of spring cloud serviceid. Modify the application.yml configuration file as follows:
api: boot: logging: # Console print log show-console-log: true # Beautify print log format-console-log-json: true # Configure Logging Admin service number discovery: service-id: logging-admin
Enable Eureka client & logging
Finally, we add notes to the XxxApplication entry class to enable Eureka Client and Logging Client, as shown below:
/** * Commodity service * * @author Heng Yu junior */ @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("{}Service started successfully.", "commodity"); } }
So far our goods and services have been ready
Create order service
Create a spring boot project named order service (it is recommended to refer to the source code, and this chapter uses Maven multi module to create).
Add dependency
Modify pom.xml to add related dependencies as follows:
<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> <!--public Openfeign Interface definition dependency--> <dependency> <groupId>org.minbox.chapter</groupId> <artifactId>common-openfeign</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies>
Order business implementation
Let's simulate an order submission scenario and create a controller named OrderController, as shown below:
/** * Order controller * * @author Heng Yu junior */ @RestController @RequestMapping(value = "/order") public class OrderController { /** * Product interface definition injection * {@link GoodClient} */ @Autowired private GoodClient goodClient; @PostMapping public String submit(Integer goodId, Integer buyCount) { Double goodPrice = goodClient.getGoodPrice(goodId); Double orderAmount = goodPrice * buyCount; //... return "Order submitted successfully, total order amount:" + orderAmount; } }
Register to Eureka Server
Register the order service we created with Eureka Server, and modify the application.yml configuration file as follows:
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
Configuring Logging Admin for escalation
We need to report the request log of order service to Logging Admin and configure it in the way of spring cloud serviceid. Modify the application.yml configuration file as follows:
api: boot: logging: # Console print log show-console-log: true # Beautify print log format-console-log-json: true # Configure Logging Admin service number discovery: service-id: logging-admin
Enable Eureka client & logging
Modify the order service entry class OrderServiceApplication, and add notes to enable Eureka Client and Logging Client, as shown below:
/** * Order service * * @author Heng Yu junior */ @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("{}Service started successfully.", ""); } }
Note explanation:
-
@Enable feignclients: this annotation is provided by Openfeign to enable the configuration of automatic scanning Client. We configure the interface of @ FeignClient annotation under the scanning package by means of basePackages (basic package name), generate the corresponding proxy implementation for each interface and add it to the Spring IOC container.
The package name of org.minbox.chapter.common.openfeign is in the common openfeign project.
Operation test
Start the project successively, Eureka server > logging admin > good service > order service.
Access the order submission address in order service through the curl command, as shown below:
➜ ~ curl -X POST http://localhost:8081/order\?goodId\=1\&buyCount\=3 Order submitted successfully, total order amount: 46.8
You can see that we have successfully obtained the total order amount. We call good service in the / order request method to obtain the unit price of the goods and calculate the total order amount.
Test point: link information transfer
We use the log information output from the console to confirm whether the transparence of the next link information (traceId, spanId) is correct.
Receive the log reported by 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":"Order submitted successfully, total order amount: 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" } ]
Log reported by 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" } ]
Results analysis:
-
The entry of the request log is order service, so there is no parentSpanId (superior cell number), and spanId (cell number) and traceId (link number) are also newly generated.
-
This request will go through the good service service. Therefore, parentSpanId is the spanId generated by order service. traceId is also generated by order service. It is transmitted through HttpHeader, indicating that it is on the same request link.
Knock on the blackboard
ApiBoot Logging supports the use of Openfeign to transmit link information, which is implemented internally through Openfeign interceptor. For the source code, see org.minbox.framework.logging.client.http.openfeign.LoggingOpenFeignInterceptor.
traceId (link number) and parentSpanId (unit number) are passed to the target access service in the form of HttpHeader. The service extracts and sets the link binding relationship through the request log interceptor.
- When trace ID is passed, HttpHeader name is: minbox logging x trace ID.
- When parent span ID is passed, HttpHeader name is: minibox logging x parent span ID
Code example
If you like this article, please point a Star for the source repository, thank you!!! The sample source code of this article can be obtained through the following ways: SpringBoot2.x/apiboot-logging-using-openfeign-transparent-traceid: