microservice send the tracing info to the zipkin tracing server. Zipkin store the information in database and provides UI.Zipkin locally on http://localhost:9411/zipkin/.Note: We can also develop spring boot app with zipkin serevr intead of using Zipking docker image.
d1-zipkin-tracing-server
d2-zipkin-currency-exchange-service
d3-zepkin-currency-conversion-service-openfeign
d4-zepkin-api-gateway-routes
Purpose / Feature
Steps
feign clients. By default requests made using feign clients are not traced.management.tracing.sampling.probability=1.0 # 1.0 -> 100%,  #SB3 logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}] #SB3RestTemplate using RestTemplateBuilder to enable tracing of requests.
new RestTemplate() will not be traced.RestTemplate and autowire in controller.Maven / External dependency
    <!-- SB3 :  Micrometer 
          > OpenTelemetry 
          > Zipkin 
    -->
    <!-- Micrometer - Vendor-neutral application observability facade. 
        Instrument your JVM-based application code without vendor lock-in.  
        Observation (Metrics & Logs) + Tracing.
    -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-observation</artifactId>
    </dependency>
    <!-- Open Telemetry
        - Open Telemetry as Bridge (RECOMMENDED)
        - Simplified Observability (metrics, logs, and traces) -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-tracing-bridge-otel</artifactId>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-zipkin</artifactId>
    </dependency>
    <!-- requres where feign client is getting used. -->
    <!-- Enables tracing of REST API calls made using Feign - SB-V3 ONLY-->
    <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-micrometer</artifactId>
    </dependency>
Code / Config changes
import org.springframework.boot.web.client.RestTemplateBuilder;    @Configuration
    public class RestTemplateConfiguration {
      @Bean
      RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
      }
    }
import org.springframework.boot.web.client.RestTemplateBuilder;    // standard controller code
    @GetMapping("/currency-conversion/from/{from}/to/{to}/quantity/{quantity}")
    public CurrencyConversion calculateCurrencyConversion(@PathVariable String from, @PathVariable String to,
        @PathVariable BigDecimal quantity) {
      logger.info("Executing CurrencyConversionController.calculateCurrencyConversion(..) API.");
      // Standardize
      from = from.toUpperCase();
      to = to.toUpperCase();
      final Map<String, String> uriVariables = new HashMap<>();
      uriVariables.put("from", from);
      uriVariables.put("to", to);
      // Send request to Currency exchange micro-service
      final ResponseEntity<CurrencyConversion> response = restTemplate.getForEntity(
          "http://localhost:8000/jpa/currency-exchange/from/{from}/to/{to}", CurrencyConversion.class,
          uriVariables);
      final CurrencyConversion currencyConversionExchange = response.getBody();
      logger.debug("Response from currency-exchange : {}", currencyConversionExchange);
      final CurrencyConversion currencyConversion = new CurrencyConversion(currencyConversionExchange.getId(), from,
          to, quantity, currencyConversionExchange.getConversionMultiples(),
          quantity.multiply(currencyConversionExchange.getConversionMultiples()),
          currencyConversionExchange.getEnvironment());
      logger.debug("Response returned : {}", currencyConversionExchange);
      return currencyConversion;
    }
    // other APIs
# logging
logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]
logging.level.com.srvivek.sboot=debug
# Start: micrometer configuration
# Add sampling configuration to define how much percentage of request will be sampled.
management.tracing.sampling.probability=1.0  # 100%
# End: micrometer configuration
docker-compose.yaml.
tabs for formatting/indentation, use only spaces.  version: '3.7'
  services:   
    naming-server:
      image: srvivek/b6-naming-service:0.0.1-SNAPSHOT
      mem_limit: 800m
      ports:
        - "8761:8761"
      networks:
        - currency-network
    
    currency-exchange:
      image: srvivek/d2-zipkin-currency-exchange-service:0.0.1-SNAPSHOT
      mem_limit: 800m
      ports:
        - "8000:8000"
      networks:
        - currency-network
      depends_on:
        - naming-server
      environment: # Properties - override defaults in appplication.properties
        EUREKA.CLIENT.SERVICE-URL.DEFAULTZONE: http://naming-server:8761/eureka
        MANAGEMENT.ZIPKIN.TRACING.ENDPOINT: http://zipkin-server:9411/api/v2/spans
    currency-conversion:
      image: srvivek/d3-zepkin-currency-conversion-service-openfeign:0.0.1-SNAPSHOT
      mem_limit: 800m
      ports:
        - "8100:8100"
      networks:
        - currency-network
      depends_on:
        - naming-server
      environment: # Properties - override defaults in appplication.properties
        EUREKA.CLIENT.SERVICE-URL.DEFAULTZONE: http://naming-server:8761/eureka
        MANAGEMENT.ZIPKIN.TRACING.ENDPOINT: http://zipkin-server:9411/api/v2/spans
    api-gateway:
      image: srvivek/d4-zepkin-api-gateway-routes:0.0.1-SNAPSHOT
      mem_limit: 800m
      ports:
        - "8765:8765"
      networks:
        - currency-network
      depends_on:
        - naming-server
      environment: # Properties - override defaults in appplication.properties
        EUREKA.CLIENT.SERVICE-URL.DEFAULTZONE: http://naming-server:8761/eureka
        MANAGEMENT.ZIPKIN.TRACING.ENDPOINT: http://zipkin-server:9411/api/v2/spans
    zipkin-server:
      image: openzipkin/zipkin
      mem_limit: 800m
      ports:
        - "9411:9411"
      networks:
        - currency-network
      restart: always #Restart if there is a problem starting up
  networks:
    currency-network: