This topic describes the Tracing Analysis feature in a Node.js 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 Node.js 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 Node.js code, you can use the SDK for OpenTelemetry to perform code instrumentation and report the data to Tracing Analysis. For the complete sample code, see nodejs-tracing-openTelemetry.

The following items describe the details of the sample code:
  • Configure the package.json dependency file in the project directory.
      "dependencies": {
        "@opentelemetry/api": "^1.0.2",
        "@opentelemetry/exporter-jaeger": "0.25.0",
        "@opentelemetry/exporter-zipkin": "0.25.0",
        "@opentelemetry/instrumentation": "0.25.0",
        "@opentelemetry/instrumentation-http": "0.25.0",
        "@opentelemetry/resources": "0.25.0",
        "@opentelemetry/semantic-conventions": "0.25.0",
        "@opentelemetry/sdk-trace-node": "0.25.0",
        "@opentelemetry/sdk-trace-base": "0.25.0"
      }
  • Report the data to Tracing Analysis.
    module.exports.handler = function(event, context, callback)
    {
        tracer = require('./tracer')('fc-tracer',context.tracing.jaegerEndpoint);
    
        var spanContext = contextFromString( context.tracing.openTracingSpanContext);
    
        startMySpan(spanContext);
    
        callback(null,'success');
    }
  • Create a tracer object that is used to create the span.
    module.exports = (serviceName,endpoint) => {
      const provider = new NodeTracerProvider({
        resource: new Resource({
          [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
        }),
      });
    
      let exporter = new JaegerExporter({endpoint:endpoint});
    
      provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
      provider.register();
    
      registerInstrumentations({
        instrumentations: [
          new HttpInstrumentation(),
        ],
      });
    
      return opentelemetry.trace.getTracer('http-example');
    };
  • Obtain the tracing information of the Context in Function Compute and convert the tracing information to a SpanContext.
    function contextFromString(value){
      const arr = value.split(`:`);
      const spanContext={
        traceId:`0000000000000000`+arr[0],
        spanId:arr[1],
        traceFlags:api.TraceFlags.SAMPLED,
        isRemote:true
      }
      return spanContext;
    }
  • 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.
    function startMySpan(spanContext){
      var FcSpan=api.trace.wrapSpanContext(spanContext);
      var ctx = api.trace.setSpan(api.ROOT_CONTEXT,FcSpan);
      tracer.startActiveSpan("fc-operation",undefined,ctx,parentSpan => {
        parentSpan.setAttribute("version","fc-v1");
        sleep(150);
        child();
        parentSpan.end()
      })
    }
    
    function child(){
      tracer.startActiveSpan("fc-operation-child",span =>{
        sleep(100);
        span.addEvent("timeout");
        span.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 nodejs-tracing.

The following items describe the details of the sample code:
  • Configure the package.json dependency file in the project directory.
    "dependencies": {
      "jaeger-client": "^3.19.0"
    }
  • Report the data to Tracing Analysis.
    module.exports.handler = function(event, context, callback)
    {
      tracer=newTracer(context);
    
      var invokeSpanContext = spanContext.fromString(context.tracing.openTracingSpanContext);
    
      startMySpan(invokeSpanContext);
    
      callback(null,'success')
    }
  • Create a tracer object based on the tracing information in the Context.
    function newTracer(context){
      var config = {
        serviceName: 'fc-tracer',
        reporter: {
          // Provide the traces endpoint; this forces the client to connect directly to the Collector and send spans over HTTP
          collectorEndpoint: context.tracing.jaegerEndpoint,
          flushIntervalMs: 10,
        },
        sampler: {
          type: "const",
          param: 1
        },
      };
      var options = {
        tags: {
          'version': 'fc-v1',
        },
      };
      var tracer = initTracer(config, options);
      return tracer
    }
  • Convert the SpanContext and create a custom span. You can also create child spans based on the span.
    function startMySpan(spanContext){
      var parentSpan = tracer.startSpan("fc-operation", {
        childOf: spanContext
      });
      sleep(150);
      child(parentSpan.context())
      parentSpan.finish();
    }
    
    function child(spanContext){
      var childSpan = tracer.startSpan("fc-operation-child", {
        childOf: spanContext
      });
      childSpan.log({event:"timeout"});
      sleep(100);
      childSpan.finish();
    }