AWS Distro for OpenTelemetry

Tracing with the AWS Distro for OpenTelemetry JavaScript SDK and X-Ray

Tracing with the AWS Distro for OpenTelemetry JavaScript SDK and X-Ray

Introduction

The AWS Distro for OpenTelemetry (ADOT) JavaScript contains some components from the upstream OpenTelemetry Javascript SDK. This guide will go over how to configure the relevant components of the ADOT SDK to send trace data to the AWS X-Ray backend.

The diagram below shows the data path for exporting traces from an application instrumented with OpenTelemetry to AWS X-Ray.

Diagram

Requirements

Node.js v10 (or later) is required to run an application using OpenTelemetry.

Note: You’ll also need to have the ADOT Collector running to export traces to AWS X-Ray. See the ADOT Collector documentation for setup instructions.




Installation

In order to trace your application, the following OpenTelemetry JavaScript packages must be installed in your application's main directory.

npm install --save \
@opentelemetry/api \
@opentelemetry/sdk-node \
@opentelemetry/exporter-trace-otlp-grpc

Install the AWS X-Ray components.

npm install --save \
@opentelemetry/id-generator-aws-xray \
@opentelemetry/propagator-aws-xray



Setting up the Global Tracer

Sending Traces to AWS X-Ray

In order to send trace data to AWS X-Ray via the ADOT Collector, you must configure the X-Ray ID generator, X-Ray propagator, and collector gRPC exporter on the global tracer provider.

1const process = require('process');
2const opentelemetry = require("@opentelemetry/sdk-node");
3const { Resource } = require("@opentelemetry/resources");
4const { SemanticResourceAttributes } = require("@opentelemetry/semantic-conventions");
5const { BatchSpanProcessor} = require('@opentelemetry/sdk-trace-base');
6const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
7const { AWSXRayPropagator } = require("@opentelemetry/propagator-aws-xray");
8const { AWSXRayIdGenerator } = require("@opentelemetry/id-generator-aws-xray");
9const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http");
10const { AwsInstrumentation } = require("@opentelemetry/instrumentation-aws-sdk");
11
12const _resource = Resource.default().merge(new Resource({
13 [SemanticResourceAttributes.SERVICE_NAME]: "js-sample-app",
14 }));
15const _traceExporter = new OTLPTraceExporter();
16const _spanProcessor = new BatchSpanProcessor(_traceExporter);
17const _tracerConfig = {
18 idGenerator: new AWSXRayIdGenerator(),
19}
20
21async function nodeSDKBuilder() {
22 const sdk = new opentelemetry.NodeSDK({
23 textMapPropagator: new AWSXRayPropagator(),
24 instrumentations: [
25 new HttpInstrumentation(),
26 new AwsInstrumentation({
27 suppressInternalInstrumentation: true
28 }),
29 ],
30 resource: _resource,
31 spanProcessor: _spanProcessor,
32 traceExporter: _traceExporter,
33 });
34 sdk.configureTracerProvider(_tracerConfig, _spanProcessor);
35
36 // this enables the API to record telemetry
37 await sdk.start();
38 // gracefully shut down the SDK on process exit
39 process.on('SIGTERM', () => {
40 sdk.shutdown()
41 .then(() => console.log('Tracing and Metrics terminated'))
42 .catch((error) => console.log('Error terminating tracing and metrics', error))
43 .finally(() => process.exit(0));
44 });
45}

Using the AWS Resource Detectors

The ADOT JavaScript SDK supports automatically recording metadata in EC2, Elastic Beanstalk, ECS, and EKS environments. To install the package containing the resource detectors, run:

npm install --save @opentelemetry/resource-detector-aws

You can add one or more of the detectors to your tracerConfig:

import { detectResources } from '@opentelemetry/resources';
import { awsEc2Detector } from '@opentelemetry/resource-detector-aws'
const resource = await detectResources({
detectors: [awsEc2Detector],
})
const tracerProvider = new NodeTracerProvider({ resource });

To see what attributes are captured and how to add other resource detectors, see the OpenTelemetry documentation.

Adding support for Metrics

The API and SDK for Metrics became stable for OpenTelemetry for JavaScript. The following piece of code initialize the OpenTelemetry SDK to use Metrics and Traces.

const _resource = Resource.default().merge(new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "js-sample-app",
}));
const _traceExporter = new OTLPTraceExporter();
const _spanProcessor = new BatchSpanProcessor(_traceExporter);
const _tracerConfig = {
idGenerator: new AWSXRayIdGenerator(),
}
const _metricReader = new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter(),
exportIntervalMillis: 1000
});
async function nodeSDKBuilder() {
const sdk = new opentelemetry.NodeSDK({
textMapPropagator: new AWSXRayPropagator(),
metricReader: _metricReader,
instrumentations: [
new HttpInstrumentation(),
new AwsInstrumentation({
suppressInternalInstrumentation: true
}),
],
resource: _resource,
spanProcessor: _spanProcessor,
traceExporter: _traceExporter,
});
sdk.configureTracerProvider(_tracerConfig, _spanProcessor);
// this enables the API to record telemetry
await sdk.start();
// gracefully shut down the SDK on process exit
process.on('SIGTERM', () => {
sdk.shutdown()
.then(() => console.log('Tracing and Metrics terminated'))
.catch((error) => console.log('Error terminating tracing and metrics', error))
.finally(() => process.exit(0));
});
}

Debug Logging

To enable debug logging for the OpenTelemetry SDK, configure the provided Diag logger as follows. Do this as early in your program as possible to capture all OpenTelemetry SDK behavior.

const { DiagConsoleLogger, DiagLogLevel, diag } = require('@opentelemetry/api');
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);

Instrumenting an Application

Warning: Some instrumentations are not yet stable and their configuration and the attributes they collect are subject to change until the instrumentation reaches 1.0 stability. It is recommended to pin a specific version of an instrumentation to avoid breaking changes.

OpenTelemetry can collect tracing data from various applications automatically using plugins. The plugins offer instrumenting popular frameworks such as Hapi, Express, Redis, GraphQL, and many more. The full list of supported libraries and installation instructions can be found on the OpenTelemetry JavaScript Contrib repo.

Instrumenting the AWS SDK

Tracing support for downstream AWS SDK calls to Amazon DynamoDB, S3, and others is provided by the OpenTelemetry AWS SDK Instrumentation.

Install the following dependency with npm:

npm install --save @opentelemetry/instrumentation-aws-sdk

Then register the AWS SDK instrumentation as follows:

1const { AwsInstrumentation } = require('@opentelemetry/instrumentation-aws-sdk');
2
3const sdk = new opentelemetry.NodeSDK({
4 instrumentations: [
5 new AwsInstrumentation({
6 suppressInternalInstrumentation: true
7 }),
8 ],
9 resource: _resource,
10 spanProcessor: _spanProcessor,
11 traceExporter: _traceExporter,
12 });

Custom Instrumentation

Creating Custom Spans

You can use custom spans to monitor the performance of internal activities that are not captured by instrumentation libraries. Note that only spans of kind Server are converted into X-Ray segments, all other spans are converted into X-Ray subsegments. For more on segments and subsegments, see the AWS X-Ray developer guide.

const { SpanKind } = require("@opentelemetry/api")
const serverSpan = tracer.startActiveSpan('server', { kind: SpanKind.SERVER }); // This span will appear as a segment in X-Ray
const internalSpan = tracer.startActiveSpan('operation', { kind: SpanKind.INTERNAL }); // This span will appear as a subsegment in X-Ray
//...
internalSpan.end();
serverSpan.end();

Adding Custom Attributes

You can also add custom key-value pairs as attributes onto your spans. Attributes are converted to metadata by default. If you configure your collector, you can convert some or all of the attributes to annotations. To read more about X-Ray annotations and metadata see the AWS X-Ray Developer Guide.

const span = tracer.startActiveSpan('sample');
span.setAttribute('key', 'value');
span.end();

Creating Metrics

Similarly to Traces, you can create custom metrics in your application using the OpenTelemetry API and SDK. Refer to Metric-Manual-Instrumentation for introduction to metric creation using OpenTelemetry JavaScript SDK.

Sample Application

See the AWS Distro for OpenTelemetry Sample Code with JavaScript SDK for instructions on setting up and using the sample app.