Before you can view the trace data of your application in the Tracing Analysis console, you must use a client to report the trace data to Tracing Analysis. This topic shows you how to use Jaeger to report Java application data.

Prerequisites

To obtain an endpoint of Jaeger or Zipkin, perform the following steps:
  1. Log on to the Tracing Analysis console.
  2. In the left-side navigation pane, click Cluster Configurations. Then, click the Access point information tab.
  3. In the top navigation bar, select a region. In the Cluster Information section, turn on Show Token.
  4. In the Client section, click Jaeger or Zipkin.

    Obtain an endpoint of Jaeger or Zipkin in the Related Information column of the table in the lower part.

    Endpoint of Jaeger or Zipkin
    Note If your application is deployed in an Alibaba Cloud production environment, use an access point of Alibaba Cloud VPC. Otherwise, use a public endpoint. Generally, use the endpoint of v2 for Zipkin. Use the endpoint of v1 only if you know Zipkin well.

Background information

Jaeger is an open source distributed tracing system. It is compatible with the OpenTracing API and has joined the Cloud Native Computing Foundation (CNCF). Jaeger is used to aggregate real-time monitoring data that is collected from multiple heterogeneous systems. The OpenTracing community provides many components that support the following Java frameworks:

To use Jaeger to report Java application data to the Tracing Analysis console, you must first instrument your application. You can instrument the application manually or by using existing components. This topic describes three methods that you can use to instrument an application:

  • Manually instrument an application.
  • Use Spring Cloud to instrument an application.
  • Use gRPC to instrument an application.
How is data reported?
  • The following figure shows how to report data without using the Jaeger agent. Report data without using the Jaeger agent
  • The following figure shows how to report data by using the Jaeger agent. Use the Jaeger agent to report data

Instrument a Java application

Before you can use Jaeger to submit the data of a Java application to Tracing Analysis, you must instrument the application. In this example, manual instrumentation is performed.

  1. Download the demo project. Go to the manualDemo directory and run the program as instructed in the README.md file.
  2. Add the following dependency for Jaeger clients to the pom.xml file:
    <dependency>
        <groupId>io.jaegertracing</groupId>
        <artifactId>jaeger-client</artifactId>
        <version>0.31.0</version>
    </dependency>
  3. Configure initialization parameters and create a tracer.

    A tracer can be used to create spans to record distributed operation time, transmit data across servers in pass-through mode by using the Extract or Inject method, or specify the current span. The tracer also contains data such as the endpoint that is used to report data, local IP address, sampling rate, and service name. You can change the sampling rate to reduce the overhead that is caused by data reporting.

    Note Replace <endpoint> with the endpoint of the region where your client resides. You can log on to the Tracing Analysis console and obtain the endpoint on the Cluster Configurations page. For more information about how to obtain the endpoint, see the Prerequisites section in this topic.
    // Replace manualDemo with your application name. 
    io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("manualDemo");
    io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration();
    // Replace <endpoint> with the endpoint that is displayed on the Cluster Configurations page of the Tracing Analysis console. 
    sender.withEndpoint("<endpoint>");
    config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1));
    config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000));
    GlobalTracer.register(config.getTracer());
  4. Record the request data.
    Tracer tracer = GlobalTracer.get();
    // Create a span. 
    Span span = tracer.buildSpan("parentSpan").withTag("myTag", "spanFirst").start();
    tracer.scopeManager().activate(span, false);
    tracer.activeSpan().setTag("methodName", "testTracing");
    // The business logic. 
    secondBiz();
    span.finish();
  5. Optional:You can run the preceding code to create a root span to record the root operation of a request. If you want to record the previous and subsequent operations of a request, specify a context.
    Tracer tracer = GlobalTracer.get();
    Span parentspan = tracer.activeSpan();
    Tracer.SpanBuilder spanBuilder = tracer.buildSpan("childSpan").withTag("myTag", "spanSecond");
    if (parentspan !=null) {
        spanBuilder.asChildOf(parentspan).start();
    }
    Span childSpan = spanBuilder.start();
    Scope scope = tracer.scopeManager().activate(childSpan); // The statement is executed once when the request starts.
    // The business logic.  The buildSpan function can be executed multiple times.
    childSpan.finish();
    tracer.activeSpan().setTag("methodName", "testCall");
    
    // The statement is executed once when the request ends.
    scope.close();
  6. Optional:To efficiently troubleshoot issues, you can add custom tags to a span. For example, you can add a custom tag to check whether an error occurs or to record the return value of a request.
    tracer.activeSpan().setTag("methodName", "testCall");
  7. In a distributed system, remote procedure call (RPC) requests are sent together with trace data. Trace data includes the values of the TraceId, ParentSpanId, SpanId, and Sampled parameters. You can call the Extract or Inject method to specify data in HTTP request headers. Process:
       Client Span                                                Server Span
    ┌──────────────────┐                                       ┌──────────────────┐
    │                  │                                       │                  │
    │   TraceContext   │           HTTP Request Headers        │   TraceContext   │
    │ ┌──────────────┐ │          ┌───────────────────┐        │ ┌──────────────┐ │
    │ │ TraceId      │ │          │ X-B3-TraceId      │        │ │ TraceId      │ │
    │ │              │ │          │                   │        │ │              │ │
    │ │ ParentSpanId │ │ Inject   │ X-B3-ParentSpanId │Extract │ │ ParentSpanId │ │
    │ │              ├─┼─────────>│                   ├────────┼>│              │ │
    │ │ SpanId       │ │          │ X-B3-SpanId       │        │ │ SpanId       │ │
    │ │              │ │          │                   │        │ │              │ │
    │ │ Sampled      │ │          │ X-B3-Sampled      │        │ │ Sampled      │ │
    │ └──────────────┘ │          └───────────────────┘        │ └──────────────┘ │
    │                  │                                       │                  │
    └──────────────────┘                                       └──────────────────┘
    1. Call the Inject method on the client to specify the context information.

      private void attachTraceInfo(Tracer tracer, Span span, final Request request) {
              tracer.inject(span.context(), Format.Builtin.TEXT_MAP, new TextMap() {
                  @Override
                  public void put(String key, String value) {
                      request.setHeader(key, value);
                  }
                  @Override
                  public Iterator<Map.Entry<String, String>> iterator() {
                      throw new UnsupportedOperationException("TextMapInjectAdapter should only be used with Tracer.inject()");
                  }
              });
          }
    2. Call the Extract method on the server to extract the context information.

      protected Span extractTraceInfo(Request request, Tracer tracer) {
          Tracer.SpanBuilder spanBuilder = tracer.buildSpan("/api/xtrace/test03");
          try {
              SpanContext spanContext = tracer.extract(Format.Builtin.TEXT_MAP, new TextMapExtractAdapter(request.getAttachments()));
              if (spanContext !=null) {
                  spanBuilder.asChildOf(spanContext);
              }
          } catch (Exception e) {
              spanBuilder.withTag("Error", "extract from request fail, error msg:" + e.getMessage());
          }
          return spanBuilder.start();
      }
                                      

Use Spring Cloud to instrument a Java application

To use Jaeger to report Java application data to the Tracing Analysis console, you must first instrument your application. This example shows you how to use Spring Cloud to instrument an application. You can use Spring Cloud to instrument the following applications:

  • @Async, @Scheduled, Executors
  • Feign, HystrixFeign
  • Hystrix
  • JDBC
  • JMS
  • Mongo
  • RabbitMQ
  • Redis
  • RxJava
  • Spring Messaging: Trace messages are sent by using message channels.
  • Spring Web (RestControllers, RestTemplates, WebAsyncTask)
  • Standard Logging: Logs are added to the current span.
  • WebSocket STOMP
  • Zuul

Perform the following steps to use Spring Cloud to instrument an application:

Note Download the demo project. Go to the springMvcDemo/webmvc4-boot directory and run the program as instructed in the README.md file.
  1. Open the pom.xml file and add JAR dependencies.
    <dependency>
       <groupId>io.opentracing.contrib</groupId>
      <artifactId>opentracing-spring-cloud-starter</artifactId>
      <version>0.2.0</version>
    </dependency>
    <dependency>
      <groupId>io.jaegertracing</groupId>
      <artifactId>jaeger-client</artifactId>
      <version>1.4.0</version>
    </dependency>
  2. Add an OpenTracing Tracer bean.
    Note Replace <endpoint> with the endpoint of the region where your client resides. You can log on to the Tracing Analysis console and obtain the endpoint on the Cluster Configurations page. For more information about how to obtain the endpoint, see the Prerequisites section in this topic.
    @Bean
    public io.opentracing.Tracer tracer() {
        io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("springFrontend");
        io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration();
        sender.withEndpoint("<endpoint>");
        config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1));
        config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000));
        return config.getTracer();
    }

Use gRPC to instrument a Java application

To use Jaeger to report Java application data to the Tracing Analysis console, you must first instrument your application. This example shows you how to use gRPC to instrument an application.

Perform the following steps to use gRPC to instrument an application:

Note Download the demo project. Go to the grpcDemo directory and run the program as instructed in the README.md file.
  1. Open the pom.xml file and add JAR dependencies.
    <dependency>
        <groupId>io.opentracing.contrib</groupId>
        <artifactId>opentracing-grpc</artifactId>
        <version>0.2.3</version>
    </dependency>
  2. Initialize the Tracer object on the server, create the ServerTracingInterceptor class, and then add an interceptor to the server.
    import io.opentracing.Tracer;
    
        public class YourServer {
    
            private int port;
            private Server server;
            private final Tracer tracer;
    
            private void start() throws IOException {
                ServerTracingInterceptor tracingInterceptor = new ServerTracingInterceptor(this.tracer);
    
                // If GlobalTracer is used: 
                // ServerTracingInterceptor tracingInterceptor = new ServerTracingInterceptor();
    
                server = ServerBuilder.forPort(port)
                    .addService(tracingInterceptor.intercept(someServiceDef))
                    .build()
                    .start();
            }
        }
  3. Initialize the Tracer object on the client, create the ClientTracingInterceptor class, and then add an interceptor to the client.
    import io.opentracing.Tracer;
    
        public class YourClient {
    
            private final ManagedChannel channel;
            private final GreeterGrpc.GreeterBlockingStub blockingStub;
            private final Tracer tracer;
    
            public YourClient(String host, int port) {
    
                channel = ManagedChannelBuilder.forAddress(host, port)
                    .usePlaintext(true)
                    .build();
    
                ClientTracingInterceptor tracingInterceptor = new ClientTracingInterceptor(this.tracer);
    
                // If GlobalTracer is used: 
                // ClientTracingInterceptor tracingInterceptor = new ClientTracingInterceptor();
    
                blockingStub = GreeterGrpc.newBlockingStub(tracingInterceptor.intercept(channel));
            }
        }
                            

FAQ

Q: Why is no data reported to the console after the demo program is run?

A: Debug the io.jaegertracing.thrift.internal.senders.HttpSender.send(Process process, List<Span> spans) method and check the return value of data reporting. If an HTTP 403 error is returned, you have specified an invalid endpoint. Change the endpoint to a valid one.