All Products
Search
Document Center

Function Compute (2.0):Tracing Analysis

Last Updated:Jul 11, 2023

This topic describes the Tracing Analysis feature in a Java 11 runtime environment.

Background

Alibaba Cloud Tracing Analysis adopts the OpenTracing standard and is compatible with open source communities. Tracing Analysis provides developers of distributed applications with various features. For example, it enables developers to query and diagnose distributed traces, dynamically discover distributed topologies, and summarize application performance in real time.

Function Compute integrates with Tracing Analysis. You can use Jaeger SDK or OpenTelemetry to upload trace information so that you can track function invocation. Tracing Analysis helps you analyze and diagnose performance bottlenecks in a serverless architecture. This improves development and diagnostics efficiency in serverless scenarios.

Overview

You can configure Tracing Analysis in the Function Compute console. For more information, see Enable Tracing Analysis.

After you enable Tracing Analysis for a service, Function Compute automatically records the time consumed in the system. The time includes the cold start time, execution time of an initializer function, and execution time of a function. For more information about system spans in the following figure, see Span name description. Tracing Analysis

You can record the time consumed for the function on the business side. For example, you can record the time taken to access services such as ApsaraDB RDS and Apsara File Storage NAS within the function by using Create custom spans.

Sample code

Tracing Analysis for Function Compute in a Java runtime environment allows you to create custom spans by using the following methods based on the Jaeger implementation of the OpenTracing specification.

Use the SDK for OpenTelemetry (recommended)

In the Java code, you can use the SDK for OpenTelemetry to perform code instrumentation data and report the data to Tracing Analysis. For the complete sample code, see java-tracing-openTelemetry.

The following items describe the details of the sample code:

  • Add dependencies to the pom.xml file.

      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>com.aliyun.fc.runtime</groupId>
          <artifactId>fc-java-core</artifactId>
          <version>1.4.1</version>
        </dependency>
        <dependency>
          <groupId>io.opentelemetry</groupId>
          <artifactId>opentelemetry-api</artifactId>
          <version>1.19.0</version>
        </dependency>
        <dependency>
          <groupId>io.opentelemetry</groupId>
          <artifactId>opentelemetry-sdk</artifactId>
          <version>1.19.0</version>
        </dependency>
        <dependency>
          <groupId>io.opentelemetry</groupId>
          <artifactId>opentelemetry-semconv</artifactId>
          <version>1.19.0-alpha</version>
        </dependency>
        <dependency>
          <groupId>io.opentelemetry</groupId>
          <artifactId>opentelemetry-exporter-jaeger-thrift</artifactId>
          <version>1.19.0</version>
        </dependency>
        <dependency>
          <groupId>io.jaegertracing</groupId>
          <artifactId>jaeger-thrift</artifactId>
          <version>1.8.1</version>
        </dependency>
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-simple</artifactId>
          <version>1.6.6</version>
        </dependency>
      </dependencies>
  • Report the data to Tracing Analysis.

    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
        String endpoint = context.getTracing().getJaegerEndpoint();
        try {
            ExampleConfiguration.initOpenTelemetry(endpoint);
        } catch (TTransportException e) {
            throw new RuntimeException(e);
        }
    
        SpanContext spanContext = contextFromString(context.getTracing().getSpanContext());
    
        startMySpan(io.opentelemetry.context.Context.current().with(Span.wrap(spanContext)));
    }
  • Create a global OpenTelemetry object that provides access to tracers.

    static OpenTelemetry initOpenTelemetry(String jaegerEndpoint) throws TTransportException {
        // Export traces to Jaeger.
        JaegerThriftSpanExporter jaegerExporter =
            JaegerThriftSpanExporter.builder()
            .setThriftSender(new Builder(jaegerEndpoint).build())
            .setEndpoint(jaegerEndpoint)
            .build();
    
        Resource serviceNameResource =
            Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "otel-jaeger-example"));
    
        // Specify a Jaeger exporter as the processor of the span.
        SdkTracerProvider tracerProvider =
            SdkTracerProvider.builder()
            .addSpanProcessor(SimpleSpanProcessor.create(jaegerExporter))
            .setResource(Resource.getDefault().merge(serviceNameResource))
            .build();
        OpenTelemetrySdk openTelemetry =
            OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal();
    
        // Disable the SDK when the Java virtual machine (JVM) exits.
        Runtime.getRuntime().addShutdownHook(new Thread(tracerProvider::close));
    
        return openTelemetry;
    }
  • Obtain the tracing information of the Context and convert the tracing information to a SpanContext.

    SpanContext contextFromString(String value) throws IOException {
        {
            if (value != null && !value.equals("")) {
                String[] parts = value.split(":");
                if (parts.length != 4) {
                    throw new RuntimeException(value);
                } else {
                    String traceId = parts[0];
                    if (traceId.length() <= 32 && traceId.length() >= 1) {
                        return SpanContext.createFromRemoteParent("0000000000000000"+parts[0], parts[1], TraceFlags.getSampled(), TraceState.getDefault());
                    } else {
                        throw new RuntimeException("Trace id [" + traceId + "] length is not withing 1 and 32");
                    }
                }
            } else {
                throw new RuntimeException();
            }
        }
    }
  • Create a tracer and use the converted context to create child spans. A span represents a continuously executable code snippet that is named and timed in an invocation trace. You can also create child spans based on the span.

    void startMySpan(io.opentelemetry.context.Context ctx){
        Tracer tracer = GlobalOpenTelemetry.getTracer("fc-Trace");
        Span parentSpan = tracer.spanBuilder("fc-operation").setParent(ctx).startSpan();
        parentSpan.setAttribute("version","fc-v1");
        try {
            TimeUnit.MILLISECONDS.sleep(150);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        child(parentSpan.storeInContext(ctx));
        parentSpan.end();
    }
    
    void child(io.opentelemetry.context.Context ctx){
        Tracer tracer = GlobalOpenTelemetry.getTracer("fc-Trace");
        Span childSpan = tracer.spanBuilder("fc-operation-child").setParent(ctx).startSpan();
        childSpan.addEvent("timeout");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        childSpan.end();
    }

Use the SDK for Jaeger

You can use the SDK for Jaeger to perform code instrumentation and report the data to Tracing Analysis. For the complete sample code, see java-tracing.

The following items describe the details of the sample code:

  • Add dependencies to the pom.xml file.

    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>com.aliyun.fc.runtime</groupId>
        <artifactId>fc-java-core</artifactId>
        <version>1.4.1</version>
      </dependency>
      <dependency>
        <groupId>io.jaegertracing</groupId>
        <artifactId>jaeger-client</artifactId>
        <version>1.8.1</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.6.6</version>
      </dependency>
    </dependencies>
  • Report the data to Tracing Analysis.

    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
    
        registerTracer(context);
    
        JaegerSpanContext spanContext = contextFromString(context.getTracing().getSpanContext());
    
        startMySpan(spanContext);
    }
  • Create a tracer object based on the tracing information in the Context.

    void registerTracer(Context context){
        io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("FCTracer");
        io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration();
        sender.withEndpoint(context.getTracing().getJaegerEndpoint());
        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());
    }
  • Convert the SpanContext and create a custom span. You can also create child spans based on the span.

    static JaegerSpanContext contextFromString(String value) throws MalformedTracerStateStringException, EmptyTracerStateStringException {
        if (value != null && !value.equals("")) {
            String[] parts = value.split(":");
            if (parts.length != 4) {
                throw new MalformedTracerStateStringException(value);
            } else {
                String traceId = parts[0];
                if (traceId.length() <= 32 && traceId.length() >= 1) {
                    return new JaegerSpanContext(0L, (new BigInteger(traceId, 16)).longValue(), (new BigInteger(parts[1], 16)).longValue(), (new BigInteger(parts[2], 16)).longValue(), (new BigInteger(parts[3], 16)).byteValue());
                } else {
                    throw new TraceIdOutOfBoundException("Trace id [" + traceId + "] length is not withing 1 and 32");
                }
            }
        } else {
            throw new EmptyTracerStateStringException();
        }
    }