JSPM

  • Created
  • Published
  • Downloads 2233
  • Score
    100M100P100Q131808F
  • License Apache-2.0

Home to the Lumigo constructs for the AWS Cloud Development Kit (AWS CDK)

Package Exports

  • @lumigo/cdk-constructs-v2
  • @lumigo/cdk-constructs-v2/lib/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@lumigo/cdk-constructs-v2) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Lumigo CDK Integrations

This repository provides means of adding Lumigo tracing as "infrastructure-as-code" to Lambda functions deployed via the AWS Cloud Development Kit (CDK) v2.

If instead of the AWS CDK v2, you are using the Serverless Framework, refer to the serverless-lumigo plugin documentation.

Installation

TypeScript / JavaScript

With yarn:

yarn install '@lumigo/cdk-constructs-v2'

With npm:

npm install '@lumigo/cdk-constructs-v2'

Supported Constructs

Supported AWS Lambda Constructs

The Lumigo CDK integration applies automated distributed tracing to the following constructs that manage AWS Lambda functions:

AWS CDK Package Constructs Notes
aws-cdk-lib/aws-lambda Function
aws-cdk-lib/aws-lambda-nodejs NodejsFunction
@aws-cdk/aws-lambda-python-alpha PythonFunction The PythonFunction is not GA in AWS CDK 2, but is supported by the Lumigo CDK integration regardless

Supported Amazon ECS Constructs

Note: The automatic instrumentation of Amazon ECS workloads is currently experimental.

The Lumigo CDK integration applies automated distributed tracing to the following constructs that manage Amazon ECS workloads:

AWS CDK Package Constructs Notes
aws-cdk-lib/aws-ecs Ec2Service Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs Ec2TaskDefinition Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs FargateService Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs FargateTaskDefinition Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns ApplicationLoadBalancedEc2Service Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns ApplicationLoadBalancedFargateService Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns ApplicationMultipleTargetGroupsEc2Service Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns ApplicationMultipleTargetGroupsFargateService Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns NetworkLoadBalancedEc2Service Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns NetworkLoadBalancedFargateService Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns NetworkMultipleTargetGroupsEc2Service Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns NetworkMultipleTargetGroupsFargateService Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns QueueProcessingEc2Service Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns QueueProcessingFargateService Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns ScheduledEc2Task Experimental Node.js and Python distributed tracing
aws-cdk-lib/aws-ecs-patterns ScheduledFargateTask Experimental Node.js and Python distributed tracing

The automated distributed tracing will work for all Node.js and Python processes dynamically linked against GNU C Library (which is used by virtually all container base images except Alpine Linux) or musl libc (for Alpine Linux-based containers).

Usage

The Lumigo CDK integration enables you to trace all the applicable constructs inside an CDK App or a Stack, as well on a function-by-function basis. The only requirement to use the Lumigo CDK integration is to have the Lumigo token stored in a way that can be accessed as a SecretValue, which supports, among other means of accessing secrets via the CDK:

Instrumenting the entire CDK application

The following code will apply Lumigo autotracing to all the supported AWS Lambda constructs:

import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue } from 'aws-cdk-lib';

const app = new App();

// Add here stacks and constructs

new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')}).traceEverything(app); // This will trace all Lambda functions managed with supported constructs

app.synth();

Currently, the autotrace injection of Amazon ECS workloads is opt-in:

import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue } from 'aws-cdk-lib';

const app = new App();

// Add here stacks and constructs

new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')}).traceEverything(app, {
  traceEcs: true,  // This activates adding tracing to Amazon ECS task definitions and services as well
});

app.synth();

Instrumenting a CDK stack

The following code will apply Lumigo autotracing to all the supported AWS Lambda constructs in the instrumented stack:

import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue, Stack, StackProps } from 'aws-cdk-lib';
import { Function } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';

export class NodejsStack extends Stack {

    constructor(scope: Construct, id: string, props: StackProps = {}) {
        super(scope, id, props);

        new Function(this, 'MyLambda', {
            code: new InlineCode('foo'),
            handler: 'index.handler',
            runtime: Runtime.NODEJS_14_X,
        });

    }

}

const app = new App();

const stack = new NodejsStack(app, 'NodejsTestStack', {
    env: {
        region: 'eu-central-1',
    }
}); 

new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')}).traceEverything(stack);

app.synth();
import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue, Stack, StackProps } from 'aws-cdk-lib';
import { FargateService } from 'aws-cdk-lib/aws-ecs';
import { Function } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';

export class NodejsStack extends Stack {

    constructor(scope: Construct, id: string, props: StackProps = {}) {
        super(scope, id, props);

        new Function(this, 'MyLambda', {
            code: new InlineCode('foo'),
            handler: 'index.handler',
            runtime: Runtime.NODEJS_14_X,
        });

        new FargateService(this, 'MyFargateService', {
            ...
        });

    }

}

const app = new App();

const stack = new NodejsStack(app, 'NodejsTestStack', {
    env: {
        region: 'eu-central-1',
    }
}); 

new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')}).traceEverything(stack, {
  traceEcs: true,  // This activates adding tracing to Amazon ECS task definitions and services as well  
});

app.synth();

Instrumenting single functions

import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue, Stack, StackProps } from 'aws-cdk-lib';
import { Function } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';

interface NodejsStackProps extends StackProps {
  readonly lumigo: Lumigo;
}

export class NodejsStack extends Stack {

    constructor(scope: Construct, id: string, props: NodejsStackProps = {}) {
        super(scope, id, props);

        const handler = new Function(this, 'MyLambda', {
            code: new InlineCode('foo'),
            handler: 'index.handler',
            runtime: Runtime.NODEJS_14_X,
        });

        props.lumigo.traceLambda(handler);
    }

}

const app = new App();

const lumigo = new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')});

const stack = new NodejsStack(app, 'NodejsTestStack', {
    env: {
        region: 'eu-central-1',
    },
    lumigo,
}); 

app.synth();

Instrumenting single ECS services

import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue, Stack, StackProps } from 'aws-cdk-lib';
import { FargateService } from 'aws-cdk-lib/aws-ecs';
import { Construct } from 'constructs';

interface NodejsStackProps extends StackProps {
  readonly lumigo: Lumigo;
}

export class NodejsStack extends Stack {

    constructor(scope: Construct, id: string, props: NodejsStackProps = {}) {
        super(scope, id, props);

        const service = new FargateService(this, 'MyFargateService', {
            ...
        });

        props.lumigo.traceEcsService(service);
    }

}

const app = new App();

const lumigo = new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')});

const stack = new NodejsStack(app, 'NodejsTestStack', {
    env: {
        region: 'eu-central-1',
    },
    lumigo,
}); 

app.synth();

Instrumenting single ECS task definitions

Instrumenting at the level of the Amazon ECS task definition enables you to share the instrumented task definition across multiple ECS services:

import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue, Stack, StackProps } from 'aws-cdk-lib';
import { FargateService, FargateTaskDefinition } from 'aws-cdk-lib/aws-ecs';
import { Construct } from 'constructs';

interface NodejsStackProps extends StackProps {
  readonly lumigo: Lumigo;
}

export class NodejsStack extends Stack {

    constructor(scope: Construct, id: string, props: NodejsStackProps = {}) {
        super(scope, id, props);

        const taskDefinition = new FargateTaskDefinition(, this 'MyFargateTaskDefinition', {
            ...
        })
        new FargateService(this, 'MyFargateService1', {
            taskDefinition: taskDefinition,
            ...
        });
        new FargateService(this, 'MyFargateService2', {
            taskDefinition: taskDefinition,
            ...
        });

        props.lumigo.traceEcsTaskDefinition(taskDefinition);
    }

}

const app = new App();

const lumigo = new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')});

const stack = new NodejsStack(app, 'NodejsTestStack', {
    env: {
        region: 'eu-central-1',
    },
    lumigo,
}); 

app.synth();

Lambda Layer Version Pinning

Unless specified otherwise, when instrumenting a Lambda function, the Lumigo CDK integration will use the latest known Lambda layer at the moment of publishing the adopted version of the @lumigo/cdk-constructs-v2 package. (It is considered bad practice in CDK Construct designs to have API calls take place inside the synth phase, so new versions of the @lumigo/cdk-constructs-v2 are regularly released, pointing at the latest layers.)

The pinning of specific layer versions can be performed at the level of the entire application or stack:

import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue } from 'aws-cdk-lib';

const app = new App();

// Add here stacks and constructs

new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')}).traceEverything(app, {
    lambdaNodejsLayerVersion: 42,  // All Lambda functions with a supported Node.js runtime will use the layer v42
    lambdaPythonLayerVersion: 1,  // All Lambda functions with a supported Python runtime will use the layer v1
});

app.synth();

Layer-version pinning can also be done function-by-function:

export class MyNodejsLambdaStack extends Stack {
  constructor(scope: Construct, id: string, props: LumigoStackProps) {
    super(scope, id, props);

    const handler = new Function(this, 'MyLambda', {
      code: new InlineCode('foo'),
      handler: 'index.handler',
      runtime: Runtime.NODEJS_14_X,
    });

    props.lumigo.traceLambda(handler, {
      layerVersion: 42,  // Which layer this is about (Node.js? Python?) is contextual to the `runtime` of the function
    });
  }
}

W3C TraceContext propagation in AWS Lambda

To be able to trace scenarios in which a Lambda function sends HTTP requests to an application instrumented with OpenTelemetry, like those using the Lumigo OpenTelemetry Distro for JS and Lumigo OpenTelemetry Distro for Python or other OpenTelemetry SDKs, the Lumigo Node.js and Python tracers can optionally add W3C TraceContext HTTP headers to outgoing requests.

Aty the tracer level, the support of W3C TraceContext is currently opt-in via the LUMIGO_PROPAGATE_W3C=true environment variable. But the Lumigo CDK integration can apply it to all your Lambda functions:

import { Lumigo } from '@lumigo/cdk-constructs-v2';
import { App, SecretValue } from 'aws-cdk-lib';

const app = new App();

// Add here stacks and constructs

new Lumigo({lumigoToken:SecretValue.secretsManager('LumigoToken')}).traceEverything(app, {
    lambdaEnableW3CTraceContext: true, // <--- This parameter set to true activates the W3C TraceContext propagation for all supported Lambda functions
});

app.synth();

Activating W3C TraceContext support is also supported on a function-by-function basis:

export class MyNodejsLambdaStack extends Stack {
  constructor(scope: Construct, id: string, props: LumigoStackProps) {
    super(scope, id, props);

    const handler = new Function(this, 'MyLambda', {
      code: new InlineCode('foo'),
      handler: 'index.handler',
      runtime: Runtime.NODEJS_14_X,
    });

    props.lumigo.traceLambda(handler, {
      enableW3CTraceContext: true,  // <--- This parameter set to true activates the W3C TraceCntext propagation for this Lambda function.
    });
  }
}

The Lumigo tracers used for ECS tracing natively use the W3C TraceContext standard, and no opt-in is necessary.

How does it work?

Like any other CDK construct, the Lumigo CDK integration contributes changes to the CloudFormation templates that CDK generates for you. The changes that the Lumigo CDK integration applies are focused on enabling out-of-the-box distributed tracing for the AWS Lambda and Amazon ECS workloads you manage via AWS CDK.

Instrumentation of AWS Lambda functions

When encountering a supported AWS Lambda-related construct, the Lumigo CDK integration:

  1. adds a Lambda layer containing the tracer for the Lambda function runtime; the layer to be used depends on the runtime, and the version to be used (Node.js, Python) is the latest at the time of the release of the version of the @lumigo/cdk-constructs-v2 package. Using a different version of the layer is supported via version pinning.
  2. sets the LUMIGO_TRACER_TOKEN environment variable with, as value, the plain-text value of the SecretValue passed on instantiation of the Lumigo object. The value should be a valid Lumigo Token.
  3. adds runtime-dependent environment variables as documented in the Lumigo AWS Lambda manual tracing documentation.

Instrumentation of Amazon ECS workloads

Note: The automatic instrumentation of Amazon ECS workloads is currently experimental.

Modifications applied to Amazon ECS task definitions

When encountering a supported Amazon ECS-related construct, the Lumigo CDK integration:

  1. Adds an ephemeral volume called lumigo-injector to the task definition
  2. Adds a container called lumigo-injector that uses a public Amazon ECR image; the image contains the latest copies of the Lumigo OpenTelemetry Distro for Node.js and Lumigo OpenTelemetry Distro for Python, alongside an LD_PRELOAD injector that is not unlike this OpenTelemetry Injector.
  3. The lumigo-injector volume is mounted to all containers in the task definition, including the lumigo-injector container.
  4. All containers other than lumigo-injector get added:
  5. A container dependency on the completion of the lumigo-injector container.
  6. The LUMIGO_TRACER_TOKEN environment variable with, as value, the plain-text value of the SecretValue passed on instantiation of the Lumigo object.
  7. The LD_PRELOAD environment variable pointing to the Lumigo Injector's lumigo_injector.so file, which performs as needed the activation of the Python or Node.js tracers delivered by the Lumigo OpenTelemetry Distros for Node.js and Python as needed.

How tracing works for Amazon ECS task definitions

The automatic instrumentation of Amazon ECS task definitions works based on the principle that the runtimes (CPython, Node.js, etc.) inside your Amazon ECS tasks can be traced with OpenTelemetry Distros like Lumigo's that have no-code activation capabilities (see Node.js, Python) if:

  1. Tracer delivery: The files of the tracers are available on the filesystem of your containers in a way that the application process has read-access to them
  2. Tracer activation: The tracer is activated therough the application's process environment

The tracer delivery is accomplished by adding the shared lumigo-injector volume, and copy into it the Lumigo OpenTelemetry Distros before all other containers can start (thanks to the container dependencies). The tracer activation is based on manipulating the process environment of your applications inside the containers via the Lumigo Injector. The Lumigo Injector is an LD_PRELOAD object that ensure that, when your application looks up environment variables like NODE_OPTIONS or AUTOWRAPT_BOOTSTRAP (used for the activation of the Lumigo OpenTelemetry Distros for Node.js and Python, respectively), the right value is returned.