ApiBoot Logging uses spring cloud openfeign to transmit link information

Keywords: Programming Spring Maven Java xml

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:

Author individual Blog Using open source framework ApiBoot Help you become Api interface service architect

Posted by exponder on Tue, 05 Nov 2019 23:12:44 -0800