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. To record the time consumed for the function on the business side, you can create custom spans. For example, you can record the time taken to access services such as ApsaraDB RDS and Apsara File Storage NAS within the function.

Procedure

You can create custom spans based on the trace of Function Compute and connect the custom spans to the trace. Tracing Analysis for Function Compute provides the following two methods to create custom spans based on the Jaeger implementation of the OpenTracing specification:

For more information about Jaeger and OpenTelemetry, see Jaeger and OpenTelemetry.

The two methods involve the following steps:

  1. Add Jaeger or OpenTelemetry dependencies to the function. For more information about how to add third-party dependencies, see Install third-party dependencies on Function Compute.
  2. Obtain the SpanContext of the trace from the function, create custom spans based on the SpanContext, and then add instrumentation before and after the code fragments whose execution time needs to be recorded.
  3. View the trace information on the Traces tab of the function details page in the Function Compute console or in the Tracing Analysis console.

Use the SDK for Jaeger

This section describes how to use Funcraft to create a custom span, and create and deploy a function in the Node.js runtime environment.

Note You can use Funcraft to install dependencies and package and deploy them to Function Compute. You can also package and deploy them in other ways. For more information about Funcraft, see Features.
  1. Create a code directory.
    mkdir jaeger-demo
  2. Go to the code directory.
    cd jaeger-demo
  3. Initialize a Node.js runtime template.
    1. Trigger the initialization script.
      fun init
      The system returns the following information:
      ? Select a template to init (Use arrow keys or type to search)
      > event-nodejs12
        event-nodejs10
        event-nodejs8
        event-nodejs6
        event-python3
        event-python2.7
        event-java8
        event-java11
        event-php7.2
        event-dotnetcore2.1
        http-trigger-nodejs12
        http-trigger-nodejs10
        http-trigger-nodejs8
        http-trigger-nodejs6
        http-trigger-python3
        http-trigger-python2.7
      (Move up and down to reveal more choices)
    2. Press the down arrow key, select event-nodejs8, and then press the Enter key.
      ? Select a template to init (Use arrow keys or type to search)
        event-nodejs12
        event-nodejs10
      > event-nodejs8
        event-nodejs6
        event-python3
        event-python2.7
        event-java8
        event-java11
        event-php7.2
        event-dotnetcore2.1
        http-trigger-nodejs12
        http-trigger-nodejs10
        http-trigger-nodejs8
        http-trigger-nodejs6
        http-trigger-python3
        http-trigger-python2.7
      (Move up and down to reveal more choices)
  4. Install Jaeger dependencies.
    fun install --runtime nodejs8 --package-type npm --save jaeger-client
    The following information is returned:
    begin pulling image registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-nodejs8:build-1.9.9, you can also use 'docker pull registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-nodejs8:build-1.9.9' to pull image by yourself.
    build-1.9.9: Pulling from aliyunfc/runtime-nodejs8
    85b1f47fba49: Already exists
    ba6bd283713a: Already exists
    817c8cd48a09: Already exists
    47cc0ed96dc3: Already exists
    8888adcbd08b: Already exists
    6f2de60646b9: Already exists
    ae482e0e9b0a: Already exists
    7415dec84f57: Already exists
    ff5113c4bbac: Pull complete
    6291e34f3366: Pull complete
    20817eb8e89c: Pull complete
    e3678ffecff1: Pull complete
    91dda143d2a4: Pull complete
    75d1af1873c8: Pull complete
    c1482d5e884e: Pull complete
    4afe2b55e98b: Pull complete
    923e0823aa03: Pull complete
    87df1bdb59d9: Pull complete
    b3ce0f864d0a: Pull complete
    971ac0161e30: Pull complete
    35720a3848e8: Pull complete
    b6c6adc1307b: Pull complete
    af503f04d53a: Pull complete
    79bb0bf8b16a: Pull complete
    446a55ebb1a5: Pull complete
    95c19f9fcde1: Pull complete
    5d7f2f97533a: Pull complete
    Digest: sha256:9615a2500ceec7597f4ae5ee64705830bf4f2a881533042547ffa737afcc4ab5
    Status: Downloaded newer image for registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-nodejs8:build-1.9.9
    Task => NpmTask
         => npm install -q --no-audit --production jaeger-client
    
    save package install commnad to /tmp/test/jaeger-demo/Funfile
  5. Write function code.
    The following code is used:
    var initTracer = require('jaeger-client').initTracer;
    var spanContext = require('jaeger-client').SpanContext;
    
    
    module.exports.handler = function(event, context, callback) 
    {
        console.log('invoking...',context.tracing);
    
        var config = {
            serviceName: 'e2e-test',
            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-e2e-tags',
            },
        };
        var tracer = initTracer(config, options);
        var invokSpanContextStr = context.tracing.openTracingSpanContext;
        console.log('spanContext', invokSpanContextStr);
        var invokSpanContext = spanContext.fromString(invokSpanContextStr);
        var span = tracer.startSpan("CustomSpan", {
            childOf: invokSpanContext
        });
        span.finish();
        var params = {
            openTracingSpanContext: context.tracing.openTracingSpanContext,
            openTracingSpanBaggages: context.tracing.openTracingSpanBaggages,
            jaegerEndpoint: context.tracing.jaegerEndpoint
        }
       callback(null,'success')
    }
  6. Update the template.yml file.
    ROSTemplateFormatVersion: '2015-09-01'
    Transform: 'Aliyun::Serverless-2018-04-03'
    Resources:
      jaeger-demo:
        Type: 'Aliyun::Serverless::Service'
        Properties:
          Description: 'Node.js jaeger demo'
          TracingConfig: Enable
        jaeger-demo:
          Type: 'Aliyun::Serverless::Function'
          Properties:
            Handler: index.handler
            Runtime: nodejs8
            CodeUri: '. /'
  7. Deploy the function to Function Compute.
    fun deploy -y
    The following information is returned:
    using template: template.yml
    using region: cn-hangzhou
    using accountId: ***********3743
    using accessKeyId: ***********mKCr
    using timeout: 10
    
    Collecting your services information, in order to caculate devlopment changes...
    
    Resources Changes(Beta version! Only FC resources changes will be displayed):
    
    ┌──────┬───────────────┬────┬──────┐
    │ Resource   │ ResourceType                 │ Action │ Property   │
    ├──────┼───────────────┼────┼──────┤
    │ jaeger-demo│ Aliyun::Serverless::Service  │ Add    │ Description│
    ├──────┼───────────────┼────┼──────┤
    │            │                              │        │ Handler    │
    │            │                              │        ├──────┤
    │ jaeger-demo│ Aliyun::Serverless::Function │ Add    │ Runtime    │
    │            │                              │        ├──────┤
    │            │                              │        │ CodeUri    │
    └──────┴───────────────┴────┴──────┘
    
    Waiting for service jaeger-demo to be deployed...
      Waiting for function jaeger-demo to be deployed...
        Waiting for packaging function jaeger-demo code...
        The function jaeger-demo has been packaged. A total of 635 files were compressed and the final size was 797.62 KB
      function jaeger-demo deploy success
    service jaeger-demo deploy success
  8. Verify the result.
    View the trace in the Function Compute console. The custom span that you created is connected to the spans of Function Compute. user-defined-span

Use OpenTelemetry

This section describes how to use Funcraft to create a custom span, and create and deploy a function in the Python runtime environment.

Note You can use Funcraft to install dependencies and package and deploy them to Function Compute. You can also package and deploy them in other ways. For more information about Funcraft, see Features.
  1. Create a code directory.
    mkdir opentelemetry-demo
  2. Go to the code directory.
    cd opentelemey-demo
  3. Initialize a Python runtime template.
    1. Trigger the initialization script.
      fun init
      The system returns the following information:
      ? Select a template to init (Use arrow keys or type to search)
      > event-nodejs12
        event-nodejs10
        event-nodejs8
        event-nodejs6
        event-python3
        event-python2.7
        event-java8
        event-java11
        event-php7.2
        event-dotnetcore2.1
        http-trigger-nodejs12
        http-trigger-nodejs10
        http-trigger-nodejs8
        http-trigger-nodejs6
        http-trigger-python3
        http-trigger-python2.7
      (Move up and down to reveal more choices)
    2. Press the down arrow key, select the event-python3.0, and then press the Enter key.
      ? Select a template to init (Use arrow keys or type to search)
        event-nodejs12
        event-nodejs10
        event-nodejs8
        event-nodejs6
      > event-python3
        event-python2.7
        event-java8
        event-php7.2
        event-dotnetcore2.1
        http-trigger-nodejs12
        http-trigger-nodejs10
        http-trigger-nodejs8
        http-trigger-nodejs6
        http-trigger-python3
        http-trigger-python2.7
        http-trigger-java8
        (Move up and down to reveal more choices)
      Note OpenTelemetry dependencies installed in Step 4 do not support Python 2.X. Therefore, you must select a template for Python 3.0.
  4. Install the OpenTelemetry dependencies.
    1. Install opentelemetry-api.
      fun install --runtime python3 --package-type pip opentelemetry-api
      The following result is returned:
      skip pulling image aliyunfc/runtime-python3.6:build-1.9.9...
      Task => PipTask
           => PYTHONUSERBASE=/code/.fun/python pip install --user --upgrade opentelemetry-api
      
      
          ╭───────────────────╮
         │                                        │
         │   Update available 3.6.20 → 3.6.21    │
         │   Run npm i @alicloud/fun to update    │
         │                                        │
         ╰────────────────────╯
    2. Install opentelemetry-sdk.
      fun install --runtime python3 --package-type pip opentelemetry-sdk
      The following result is returned:
      skip pulling image aliyunfc/runtime-python3.6:build-1.9.9...
      Task => PipTask
           => PYTHONUSERBASE=/code/.fun/python pip install --user --upgrade opentelemetry-sdk
    3. Install opentelemetry-exporter-jaeger.
      fun install --runtime python3 --package-type pip opentelemetry-exporter-jaeger
      The following result is returned:
      skip pulling image aliyunfc/runtime-python3.6:build-1.9.9...
      Task => PipTask
           => PYTHONUSERBASE=/code/.fun/python pip install --user --upgrade opentelemetry-exporter-jaeger
  5. Write function code.
    vim index.py
    The following code is used:
    # -*- coding: utf-8 -*-
    import logging
    import time
    from opentelemetry import trace
    from opentelemetry.exporter import jaeger
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchExportSpanProcessor
    from opentelemetry.trace import set_span_in_context
    
    trace.set_tracer_provider(TracerProvider())
    tracer = trace.get_tracer(__name__)
    
    
    def handler(event, context):
        logger = logging.getLogger()
        logger.info("start to init tracer")
        init_tracer(context.tracing.jaeger_endpoint)
        jaeger_span_context = context.tracing.span_context
        fc_invocation_span = get_fc_span(jaeger_span_context)
        with fc_invocation_span as parent:
            print('Hello world!')
        context = set_span_in_context(parent)
        child = tracer.start_span("child", context=context)
        # print(child)
        time.sleep(0.1)
        child.end()
        return 'hello world'
    
    
    def init_tracer(endpoint):
        jaeger_exporter = jaeger.JaegerSpanExporter(
            service_name='opentelemetry-service',
            collector_endpoint=endpoint,
            transport_format='thrift'  # optional
        )
    
        # Create a BatchExportSpanProcessor and add the exporter to it
        span_processor = BatchExportSpanProcessor(jaeger_exporter)
    
        # add to the tracer
        trace.get_tracer_provider().add_span_processor(span_processor)
    
    
    def get_fc_span(jaeger_span_context):
        jaeger_span_context_arr = jaeger_span_context.split(":")
        tid = int(jaeger_span_context_arr[0], 16)
        sid = int(jaeger_span_context_arr[1], 16)
        fid = int(jaeger_span_context_arr[3], 16)
    
        span_context = trace.SpanContext(
            trace_id=tid,
            span_id=sid,
            is_remote=True,
            trace_flags=trace.TraceFlags(fid),
            trace_state=[],
        )
        return trace.DefaultSpan(span_context)
  6. Update the template.yml file.
    ROSTemplateFormatVersion: '2015-09-01'
    Transform: 'Aliyun::Serverless-2018-04-03'
    Resources:
      opentelemetry-demo:
        Type: 'Aliyun::Serverless::Service'
        Properties:
          Description: 'helloworld'
          TracingConfig: Enable
        opentelemetry-demo:
          Type: 'Aliyun::Serverless::Function'
          Properties:
            Handler: index.handler
            Runtime: python3
            CodeUri: '. /'
  7. Deploy the function to Function Compute.
    fun deploy -y
    The following result is returned:
    using template: template.yml
    using region: cn-hangzhou
    using accountId: ***********3743
    using accessKeyId: ***********mKCr
    using timeout: 10
    
    Collecting your services information, in order to caculate devlopment changes...
    
    Resources Changes(Beta version! Only FC resources changes will be displayed):
    
    ┌──────────┬───────────────┬────┬───────┐
    │ Resource           │ ResourceType                 │ Action │ Property     │
    ├──────────┼───────────────┼────┼───────┤
    │                    │                              │        │ Description  │
    │ opentelemetry-demo │ Aliyun::Serverless::Service  │ Add    ├───────┤
    │                    │                              │        │ TracingConfig│
    ├──────────┼───────────────┼────┼───────┤
    │                    │                              │        │ Handler      │
    │                    │                              │        ├───────┤
    │ opentelemetry-demo │ Aliyun::Serverless::Function │ Add    │ Runtime      │
    │                    │                              │        ├───────┤
    │                    │                              │        │ CodeUri      │
    └──────────┴───────────────┴────┴───────┘
    
    Waiting for service opentelemetry-demo to be deployed...
      Waiting for function opentelemetry-demo to be deployed...
        Waiting for packaging function opentelemetry-demo code...
        The function opentelemetry-demo has been packaged. A total of 529 files were compressed and the final size was 5.12 MB
      function opentelemetry-demo deploy success
    service opentelemetry-demo deploy success
  8. Verify the result.
    View the trace in the Function Compute console. The custom span that you created is connected to the spans of Function Compute. user-defined-span-opentelemetry