Enable OpenTelemetry bridge
OpenTelemetry is a collection of tools, APIs, and SDKs. You can use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis to get insights into your software's performance and behavior.
Enable OpenTelemetry
OpenTelemetry interoperability is disabled by default in Dynatrace, but you can manually enable it. For example, to enable OpenTelemetry when you configure the Dynatrace AWS Lambda extension using the environment variables, add DT_OPEN_TELEMETRY_ENABLE_INTEGRATION=true
.
Enabling OpenTelemetry interoperability connects the Dynatrace AWS Lambda extension to the OpenTelemetry API. This makes it possible to use the instrumentation packages and extensions available for their respective OpenTelemetry implementation, which enables monitoring of technologies like databases or messaging frameworks that aren't supported by Dynatrace AWS Lambda extension out of the box.
Note: If an OpenTelemetry SDK already exists, it will be evicted by the Dynatrace AWS Lambda extension if this option is enabled.
OpenTelemetry Python
For OpenTelemetry interoperability to be enabled, the installed Python OpenTelemetry API version needs to be compatible with the Dynatrace AWS Lambda extension.
The following table lists the compatible versions:
OneAgent version | Maximum OpenTelemetry API version |
---|---|
1.233+ | 1.7.x |
1.235+ | 1.8.x |
1.239+ | 1.9.x |
1.243+ | 1.11.x |
1.249+ | 1.12.x |
1.253+ | 1.13.x |
1.257+ | 1.14.x |
1.259+ | 1.15.x |
Using an OpenTelemetry Python instrumentation
OpenTelemetry for Python provides several instrumentation packages in their OpenTelemetry Python contributions repository.
The code snippet below shows how to instrument PostgreSQL calls to your Python Lambda function by using the aiopg instrumentation package.
import json
import aiopg
from opentelemetry.instrumentation.aiopg import AiopgInstrumentor
AiopgInstrumentor().instrument()
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': json.dumps(execute_query())
}
def execute_query():
result = []
with aiopg.connect(database='my_db') as conn:
with conn.cursor() as cur:
cur.execute("SELECT 'hello db';")
for row in cur:
result.append(row)
return result
To instrument boto3, the AWS SDK for Python, OpenTelemetry provides the botocore instrumentation package. The code snippet below shows how this instrumentation can be used to add observability for calls to a DynamoDB database.
import boto3
import json
from opentelemetry.instrumentation.botocore import BotocoreInstrumentor
BotocoreInstrumentor().instrument()
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('MyTable')
def lambda_handler(event, handler):
result = table.get_item(Key={'mykey': 42})
return {
"statusCode": 200,
"answer": json.dumps(result.get("Item"))
}
Starting with Dynatrace version 1.244+, Dynatrace provides support for DynamoDB database service.
Example DynamoDB service page after running the above code snippet:
Using the OpenTelemetry Python API
The code snippet below shows how OpenTelemetry Python can be used in an SDK-like approach to trace additional operations that aren't covered by an instrumentation package.
import json
from opentelemetry import trace
def lambda_handler(event, context):
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("do work"):
# do work
with tracer.start_as_current_span("do some more work") as span:
span.set_attribute("foo", "bar")
# do some more work
return {
'statusCode': 200,
'body': json.dumps('Hello from Hello world from OpenTelemetry Python!')
}
These spans are displayed on the Code level tab.
Tracing AWS SQS and SNS messages with Python
SQS: OneAgent version 1.253+ SNS: OneAgent version 1.257+
This section explains how AWS SQS and SNS messages can be traced in Python so the Dynatrace AWS Lambda extension can collect the traces.
The Dynatrace AWS Lambda extension supports the tracing of AWS SQS and SNS messages by integrating open-source instrumentations.
Install the required dependencies
Send an SQS/SNS message
Receive an SQS/SNS message
Install the required dependencies
Note: If you install the dependencies into a Lambda function or layer, you can use the -t
option to specify a target directory where the installed packages should be copied.
pip install -U "opentelemetry-api>=1.12" "opentelemetry-instrumentation-boto3sqs>=0.34b0"
Note that at this point, opentelemetry-instrumentation-boto3sqs
is a separate package from opentelemetry-instrumentation-botocore
. The latter instruments all AWS SDK calls, but lacks enhanced support for SQS.
pip install -U "opentelemetry-instrumentation-botocore>=0.36b0"
Send an SQS/SNS message
Example sending SQS messages with Python:
from opentelemetry.instrumentation.boto3sqs import Boto3SQSInstrumentor
Boto3SQSInstrumentor().instrument()
import json
import boto3
from datetime import datetime
QUEUE_URL = "<Your SQS Queue URL>"
sqs = boto3.client("sqs")
def lambda_handler(event, context):
sent = []
for i in range(5):
res = sqs.send_message(QueueUrl=QUEUE_URL, MessageBody=f"hello #{i} at {datetime.now()}")
sent.append(res["MessageId"])
return {
"statusCode": 200,
"body": json.dumps({"produced_messages": sent})
}
Example sending SNS messages with Python:
from opentelemetry.instrumentation.botocore import BotocoreInstrumentor
BotocoreInstrumentor().instrument()
import json
import boto3
from datetime import datetime
TOPIC_ARN = "<Your SNS topic ARN>"
sns = boto3.client("sns")
def lambda_handler(event, context):
res = sns.publish(TopicArn=TOPIC_ARN, Message=f"hello at {datetime.now()}")
return {
"statusCode": 200,
"body": json.dumps({"produced_message": res["MessageId"]})
}
The boto3 package is available out of the box if the code runs in AWS Lambda, however, you can also install it using pip install -U boto3
.
This code defining a function named lambda_handler
can be used
-
Inside AWS Lambda (we recommend monitoring it with our AWS Lambda layer)
-
Outside AWS Lambda (monitoring is performed with OpenTelemetry and exported to Dynatrace via OTLP/HTTP ingest)
Note: You may want to remove the function parameters and return value.
Receive an SQS/SNS message
See below for examples of the following:
- Receive messages with AWS Lambda SQS trigger (configured outside the code)
- Call the SQS receive API in code (using boto3)
Notes:
- If you invoke the sender and have deployed the SQS-triggered example, it will be invoked automatically by SQS.
- If you have deployed the example that uses the
receive
API in code, you need to invoke it manually and it will attempt to read all messages from the queue. - If your SQS queue is subscribed to an SNS topic, the examples below might need to be adapted. For details, see Tracing SQS messages forwarded from an SNS topic.
Example receiving messages with AWS Lambda SQS trigger
If you use an AWS Lambda with an SQS trigger monitored with the Dynatrace AWS Lambda extension, receiving messages works out of the box. However, the tracer can select only a single parent, and if your Lambda function receives a batch of multiple messages, you need to manually create spans to process every single message if you want to track them separately and have them linked to the sender.
from pprint import pformat
import boto3
import json
from opentelemetry import trace, propagate
from opentelemetry.semconv.trace import SpanAttributes, MessagingOperationValues
tracer = trace.get_tracer("lambda-sqs-triggered")
def lambda_handler(event, context):
recvcount = 0
print("Trigger", pformat(event))
messages = event.get("Records") or ()
# Lambda SQS event uses lowerCamelCase in its attribute names
for msg in messages:
recvcount += 1
print("Processing", msg["messageId"])
parent = _extract_parent(msg, from_sns_payload=False)
with tracer.start_as_current_span("manual-trigger-process", context=parent, kind=trace.SpanKind.CONSUMER, attributes={
SpanAttributes.MESSAGING_MESSAGE_ID : msg["messageId"],
SpanAttributes.MESSAGING_URL : msg["eventSourceARN"],
SpanAttributes.MESSAGING_SYSTEM : msg["eventSource"],
SpanAttributes.MESSAGING_OPERATION : MessagingOperationValues.PROCESS.value,
}):
# ... Here your actual processing would go...
pass
print("Processed", recvcount, "messages")
def _extract_parent(msg, from_sns_payload=False):
if from_sns_payload:
try:
body = json.loads(msg.get("body", "{}"))
except json.JSONDecodeError:
body = {}
carrier = {key: value["Value"] for key, value in body.get("MessageAttributes", {}).items() if "Value" in value}
else:
carrier = {key: value["stringValue"] for key, value in msg.get("messageAttributes", {}).items() if "stringValue" in value}
return propagate.extract(carrier)
For the manual-trigger-process
span to work correctly, you need to configure the Dynatrace AWS Lambda extension to allow parent spans to be set manually. This can be done by setting the DT_OPEN_TELEMETRY_ALLOW_EXPLICIT_PARENT
environment variable to true
:
DT_OPEN_TELEMETRY_ALLOW_EXPLICIT_PARENT=true
Alternatively, you can configure it in dtconfig.json
by setting the following field to true
:
{
"OpenTelemetry": {
"AllowExplicitParent": "true"
}
}
For details on how to configure the Dynatrace AWS Lambda extension, see Trace Python, Node.js, and Java Lambda functions.
The resulting path looks as follows:
The invoked Lambda function is a child of one of the messages by which it's triggered. Since there can only be one parent, the other manual-trigger–process spans aren't linked directly to the Lambda invocation in which they are handled. Often, there's more than one Lambda invocation node for a batch of messages. In those cases, AWS distributed the batch over multiple Lambda invocations. This can happen even if the messages are delivered within your configured batch window time and number less than your configured batch size.
Example calling the SQS receive APIs manually
from opentelemetry.instrumentation.boto3sqs import Boto3SQSInstrumentor
Boto3SQSInstrumentor().instrument()
from pprint import pformat
import boto3
import json
from opentelemetry import trace, propagate
from opentelemetry.semconv.trace import SpanAttributes, MessagingOperationValues
QUEUE_URL = '<Your SQS Queue URL>'
sqs = boto3.client("sqs")
tracer = trace.get_tracer("lambda-receive-function")
def lambda_handler(event, context):
recvcount = 0
while True:
msg_receive_result = sqs.receive_message(
MaxNumberOfMessages=10,
QueueUrl=QUEUE_URL,
WaitTimeSeconds=1, # WaitTime of zero would use sampled receive, may return empty even if there is a message
# This argument is only required if you do not use the boto3sqs instrumentation:
#MessageAttributeNames=list(propagate.get_global_textmap().fields)
)
print("Received", pformat(msg_receive_result))
if not msg_receive_result.get('Messages'):
break
messages = msg_receive_result.get("Messages")
# receive result uses PascalCase in its attribute names
for msg in messages:
recvcount += 1
print("Processing", msg["MessageId"])
parent = _extract_parent(msg, from_sns_payload=False)
with tracer.start_as_current_span("manual-receive-process", context=parent, kind=trace.SpanKind.CONSUMER, attributes={
SpanAttributes.MESSAGING_MESSAGE_ID: msg["MessageId"],
SpanAttributes.MESSAGING_URL: QUEUE_URL,
SpanAttributes.MESSAGING_SYSTEM: "aws.sqs",
SpanAttributes.MESSAGING_OPERATION: MessagingOperationValues.PROCESS.value,
}):
# ... Here your actual processing would go...
print("Delete result", sqs.delete_message(
QueueUrl=QUEUE_URL,
ReceiptHandle=msg['ReceiptHandle'],
))
print("Processed", recvcount, "messages")
def _extract_parent(msg, from_sns_payload=False):
if from_sns_payload:
try:
body = json.loads(msg.get("Body", "{}"))
except json.JSONDecodeError:
body = {}
carrier = {key: value["Value"] for key, value in body.get("MessageAttributes", {}).items() if "Value" in value}
else:
carrier = {key: value["StringValue"] for key, value in msg.get("MessageAttributes", {}).items() if "StringValue" in value}
return propagate.extract(carrier)
This code can also be used outside a Lambda function and monitored with pure OpenTelemetry instead of the Dynatrace AWS Lambda extension.
This example uses the boto3sqs instrumentation. If you don't want to use it, you need to uncomment the
MessageAttributeNames
argument in the receive_message
function, otherwise, SQS will omit data required for
linking the message to its sender from the retrieved data.
Creating the manual-receive-process
span manually is necessary because the boto3sqs instrumentation doesn't set the sender as a parent for the processing span, but uses OpenTelemetry links, which are currently not supported by Dynatrace. For the linking of the manual-receive-process
span to work correctly, you need to configure the Dynatrace AWS Lambda extension to allow setting parent spans manually. See the previous example for guidance.
Invoking first the code that sends SQS messages, then the manual receive code, deployed as Lambda functions, results in two traces:
-
The first trace shows the flow of the message from the sender to the processor:
There are additional
Requests to public networks
nodes because the boto3 package uses HTTP requests to send SQS messages, which are captured by Dynatrace HTTP instrumentation.You'll notice that the invocation and receive node of the second Lambda invocation are missing from this trace, even though the
manual-receive-process
nodes are there. This is because the Lambda function was triggered independently of the message flow, and just happened to receive the message as part of its handler code. -
The second trace in Dynatrace shows the Lambda invocation until it's cut in two by setting the explicit parent:
Tracing SQS messages forwarded from an SNS topic
For SNS messages that are forwarded to SQS, the message format depends on the raw message delivery configuration on the SNS subscription.
- If raw message delivery is disabled, the SNS message and its
MessageAttributes
are delivered as a serialized JSON string in the body of the received SQS message. To correctly link the receive span, the parent needs to be extracted from theMessageAttributes
of the serialized SNS message.- For both examples (calling the SQS receive APIs manually and receiving messages with AWS Lambda SQS trigger), you need to set the value for the
from_sns_payload
parameter toTrue
when calling the_extract_parent
method.
- For both examples (calling the SQS receive APIs manually and receiving messages with AWS Lambda SQS trigger), you need to set the value for the
- If raw message delivery is enabled on the SNS subscription, the SNS message attributes are converted to SQS message attributes and the parent can be directly extracted from the
MessageAttributes
of the SQS message.- No additional settings are needed for the examples above.
AWS Lambda functions that are triggered by SNS are supported out of the box when monitored with the Dynatrace AWS Lambda extension.
SNS topics can be configured via a subscription to forward messages to an SQS queue. Messages in the SQS queue can then be consumed by a Lambda function. Tracing the received messages in the SQS-triggered AWS Lambda works out of the box when AWS Lambda is monitored with the Dynatrace AWS Lambda extension. However, the tracer can only select a single parent, and if your Lambda function receives batches of multiple messages, special handling is required to track each message separately. For details, see Receive an SQS message.
OpenTelemetry JavaScript (Node.js)
OneAgent version 1.229+
For OpenTelemetry interoperability to be enabled, the installed JavaScript OpenTelemetry API version needs to be compatible with Dynatrace AWS Lambda extension.
The following table lists the compatible versions:
OneAgent version | Maximum OpenTelemetry API version |
---|---|
1.229+ | 1.0.x |
1.241+ | 1.1.x |
1.257+ | 1.2.x |
1.259+ | 1.3.x |
Using an OpenTelemetry Node.js instrumentation
OpenTelemetry for JavaScript provides several instrumentation packages in their OpenTelemetry JavaScript contributions repository. Some instrumentations, such as @opentelemetry/instrumentation-http and @opentelemetry/instrumentation-aws-lambda are automatically suppressed, as they might interfere with the Dynatrace HTTP and Lambda instrumentations. Using @opentelemetry/auto-instrumentations-node is also discouraged, as it includes many different instrumentations.
Note: Dynatrace AWS Lambda extension handles the configuration of all necessary OpenTelemetry SDK components and the registration of a TracerProvider, so you don't need to register another TracerProvider.
The code snippet below shows how to instrument PostgreSQL calls to your Node.js Lambda function by using the opentelemetry-instrumentation-pg instrumentation package.
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { PgInstrumentation } = require('@opentelemetry/instrumentation-pg');
// You must create the PgInstrumentation (and other instrumentations)
// before loading any corresponding modules, such as `require('pg')`.
registerInstrumentations({
instrumentations: [
new PgInstrumentation(),
],
});
const { Client } = require('pg');
exports.handler = async function myHandler(event, context) {
let client;
try {
client = new Client(/* DB connection information */);
await client.connect();
const result = await client.query('SELECT * FROM users;');
return result.rows;
} finally {
client?.end();
}
}
To instrument the AWS SDK for JavaScript, OpenTelemetry provides the opentelemetry/instrumentation-aws-sdk
instrumentation package.
The code snippet below shows how this instrumentation can be used to add observability for calls to a DynamoDB database.
const AWS = require('aws-sdk');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { AwsInstrumentation } = require('@opentelemetry/instrumentation-aws-sdk');
registerInstrumentations({
instrumentations: [
new AwsInstrumentation()
]
});
exports.handler = function(event, context) {
const ddb = new AWS.DynamoDB();
const dbParamsGetDelete = {
TableName: 'E2E_test_table',
Key: {
'svnr': { N: '1234'}
}
};
ddb.getItem(dbParamsGetDelete, function(err, data) {
if (err) {
console.error('Error', err);
} else {
console.log('Success', data.Item);
}
});
};
Starting with Dynatrace version 1.244+, Dynatrace provides support for DynamoDB database service.
Example DynamoDB service page after running the above code snippet:
Using the OpenTelemetry Node.js API
The code snippet below shows how OpenTelemetry JavaScript can be used in an SDK-like approach to trace additional operations that aren't covered by an instrumentation package.
const opentelemetry = require('@opentelemetry/api');
const tracer = opentelemetry.trace.getTracer('my-package-name');
exports.handler = function(event, context) {
// create a span using the OTel API
const span = tracer.startSpan('do some work');
span.setAttribute('foo', 'bar');
span.end();
// ...
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Node.js'),
};
return response;
};
Tracing AWS SQS and SNS messages with Node.js
SQS: OneAgent version 1.253+ SNS: OneAgent version 1.257+
This section explains how AWS SQS messages can be traced for Dynatrace AWS Lambda extension to collect the traces.
Dynatrace supports tracing of AWS SQS and SNS messages by integration of the @opentelemetry/instrumentation-aws-sdk package.
Install the required dependencies
Set up tracing
Send an SQS/SNS message
Receive an SQS/SNS message
Install the required dependencies
npm install @opentelemetry/api @opentelemetry/instrumentation-aws-sdk @opentelemetry/instrumentation aws-sdk
Set up tracing
Use the following code to set up tracing for sending SQS messages to an SQS queue from a Dynatrace monitored Node.js application:
const { AwsInstrumentation } = require('@opentelemetry/instrumentation-aws-sdk');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
registerInstrumentations({
instrumentations: [
new AwsInstrumentation()
]
});
Note: Make sure that the tracing setup is done before the aws-sdk module is required (tested with version 2.1228.0).
Send an SQS/SNS message
Example using the Node.js HTTP server:
When you make a request to the HTTP server, a message is sent to an SQS queue or SNS topic. If you send a message when no root span exists, make sure to create the root span manually. Creating the root span manually isn't needed in this example, as the tracer already creates a root span for the incoming HTTP request. For details on the manual span creation with OpenTelemetry, see OpenTelemetry traces with OneAgent.
const http = require("http");
const AWS = require('aws-sdk');
const sqs = new AWS.SQS();
const sns = new AWS.SNS();
const server = http.createServer((req, res) => {
const messageSendCallback = function (err, message) {
if (err) {
console.log("failed to send a message: " + err);
res.writeHead(500);
res.end("failure");
} else {
console.log("Success", message.MessageId);
res.writeHead(200);
res.end("success");
}
}
if (req.url === "/send-sqs-message") {
const params = {
DelaySeconds: 10,
MessageBody: "[your payload]",
QueueUrl: "[your SQS-queue URL]"
};
sqs.sendMessage(params, messageSendCallback);
} else if (req.url === "/send-sns-message") {
const params = {
Message: "[your payload]",
TopicArn: "[your SNS-topic ARN]"
};
sns.publish(params, messageSendCallback);
} else {
res.writeHead(404);
res.end("not found");
}
});
server.on("close", () => { console.log("Closing server") });
server.listen(8004, () => {
console.log("server started!");
});
A request to the /send-sqs-message
path should produce traces as shown below. The second node in the distributed trace named sqs-minimal-sample-nodejs-receiver-trigger send
represents the sent SQS message and is generated by the aws-sdk instrumentation.
Note: There's an additional Requests to public networks
node because the aws-sdk package uses HTTP requests to send SQS messages, which are captured by the OneAgent HTTP instrumentation. The third node in the distributed trace sqs-minimal-sample-nodejs-receiver in eu-central-1
comes from the AWS Lambda function subscribed to the SQS queue, which is monitored by the Dynatrace AWS Lambda extension.
Example using an AWS Lambda function:
You can send an SQS or SNS message from an AWS Lambda function monitored by the Dynatrace AWS Lambda extension.
const AWS = require('aws-sdk');
exports.handler = function (event, context, callback) {
const sqs = new AWS.SQS();
const params = {
DelaySeconds: 10,
MessageBody: "[your payload]",
QueueUrl: "[your SQS-queue URL]"
};
sqs.sendMessage(params, function (err, data) {
if (err) {
context.succeed({
statusCode: 500,
body: err,
});
} else {
console.log("SQS-Success", data.MessageId);
context.succeed({
statusCode: 200,
body: "SQS-Success",
});
}
});
}
The resulting distributed trace is similar to the Node.js application example:
const AWS = require('aws-sdk');
exports.handler = function (event, context, callback) {
const sns = new AWS.SNS();
const params = {
Message: "[your payload]",
TopicArn: "[your SNS-topic ARN]"
};
sns.publish(params, function (err, data) {
if (err) {
context.succeed({
statusCode: 500,
body: err,
});
} else {
console.log("SNS-Success", data.MessageId);
context.succeed({
statusCode: 200,
body: "SNS-Success",
});
}
});
}
Receive an SQS/SNS message
In both SQS send examples, the subscribed AWS Lambda function sqs-minimal-sample-receiver
is triggered by AWS SQS. The Dynatrace AWS Lambda extension extracts the parent from the SQS message and creates the Lambda span. For a more complex scenario, where a batch of multiple messages is received, only the last message will be considered and used for parent propagation.
One of the ways to propagate parents from the batch of multiple incoming messages is to manually create spans with the parent from each message. To do that, you first need to configure the Dynatrace AWS Lambda extension to allow setting parent spans manually. This can be done by setting the DT_OPEN_TELEMETRY_ALLOW_EXPLICIT_PARENT
environment variable to true
:
DT_OPEN_TELEMETRY_ALLOW_EXPLICIT_PARENT: true
Alternatively, you can configure it in dtconfig.json
by setting the following field to true
.
{
"OpenTelemetry": {
"AllowExplicitParent": "true"
}
}
For more details about how to configure the Dynatrace AWS Lambda extension, see Trace Python, Node.js, and Java Lambda functions.
By setting this option to true
, new spans can be started with the parent span extracted from each received SQS message, as demonstrated in the following example:
const { propagation, ROOT_CONTEXT, trace, SpanKind } = require("@opentelemetry/api");
const { MessagingOperationValues, SemanticAttributes } = require("@opentelemetry/semantic-conventions");
const AWS = require("aws-sdk");
const queueUrl = "[sqs queue url]";
const tracer = trace.getTracer("my-receiver");
exports.handler = async (event) => {
const sqs = new AWS.SQS();
await new Promise((resolve, reject) => {
const receiveParams = {
MaxNumberOfMessages: 10,
QueueUrl: queueUrl,
// MessageAttributeNames not needed if @opentelemetry/instrumentation-aws-sdk is used
MessageAttributeNames: propagation.fields()
};
sqs.receiveMessage(receiveParams, function (err, data) {
if (err) {
console.log("ERROR: ", err);
reject(err);
} else if (data.Messages?.length) {
data.Messages.forEach((msg) => {
console.log("message received:", msg.MessageId)
// manual span creation
const ctx = extractParent(msg, /*fromSnsPayload=*/ false);
const spanAttributes = {
[SemanticAttributes.MESSAGING_MESSAGE_ID]: msg.MessageId,
[SemanticAttributes.MESSAGING_URL]: queueUrl,
[SemanticAttributes.MESSAGING_SYSTEM]: "aws.sqs",
[SemanticAttributes.MESSAGING_OPERATION]: MessagingOperationValues.PROCESS,
};
const span = tracer.startSpan("received message", { kind: SpanKind.CONSUMER, attributes: spanAttributes }, ctx);
// ... Here your actual processing would go...
span.end();
const deleteParams = {
QueueUrl: queueUrl,
ReceiptHandle: msg.ReceiptHandle
};
sqs.deleteMessage(deleteParams, function (err, data) {
if (err) {
console.log("Delete Error", err);
} else {
console.log("Message Deleted", data);
}
});
});
}
resolve();
});
});
};
function extractParent(msg, fromSnsPayload=false) {
let valueKey = "StringValue"
if (fromSnsPayload) {
valueKey = "Value";
try {
msg = JSON.parse(msg.Body)
} catch {
msg = {}
}
}
const carrier = {};
Object.keys(msg.MessageAttributes || {}).forEach((attrKey) => {
carrier[attrKey] = msg.MessageAttributes[attrKey]?.[valueKey];
});
return propagation.extract(ROOT_CONTEXT, carrier)
};
In the example above, aws-sdk
is used to subscribe and receive messages from an SQS queue. For each incoming message, the parent span is extracted from the message attributes, and a new received message
span with the extracted parent is created. If your SQS queue is subscribed to an SNS topic, the example above might need to be adapted. For details, see Tracing SQS messages forwarded from an SNS topic.
The resulting distributed trace with two messages sent to a queue looks as below.
Tracing SQS messages forwarded from an SNS topic
For SNS messages that are forwarded to SQS, the format of the message depends on the raw message delivery configuration on the SNS subscription.
- If raw message delivery is disabled, the SNS message and its
MessageAttributes
are delivered as a serialized JSON string in the body of the received SQS message. To correctly link the receive span, the parent needs to be extracted from theMessageAttributes
of the serialized SNS message.- For the
receive
example, the value for thefromSnsPayload
parameter needs to be set totrue
when calling theextractParent
method.
- For the
- If raw message delivery is enabled on the SNS subscription, the SNS message attributes are converted to SQS message attributes and the parent can be directly extracted from the
MessageAttributes
of the SQS message.- No additional settings are needed for the
receive
example.
- No additional settings are needed for the
AWS Lambda functions that are triggered by SNS are supported out of the box when monitored with the OneAgent layer.
SNS topics can be configured via a subscription to forward messages to an SQS queue. Messages in the SQS queue can then be consumed by a Lambda function. Tracing the received messages in the SQS-triggered AWS Lambda works out of the box when AWS Lambda is monitored with the Dynatrace AWS Lambda extension. However, the tracer can only select a single parent, and if your Lambda function receives batches of multiple messages, special handling is required to track each message separately. For details, see Receive an SQS message.
OpenTelemetry Java
OneAgent version 1.225+
Using the OpenTelemetry Java API
The code snippet below shows how OpenTelemetry Java can be used in an SDK-like approach to trace additional operations that aren't covered by Dynatrace out of the box.
@Override
public String handleRequest(Object input, Context context) {
Tracer tracer = GlobalOpenTelemetry.getTracer("instrumentation-library-name", "1.0.0");
Span span = tracer.spanBuilder("do some work").startSpan();
try {
span.setAttribute("foo", "bar");
// ....
return "Hello from OpenTelemetry Java!";
} finally {
span.end();
}
}
Note: The Dynatrace AWS Lambda extension captures only spans created via tracers from GlobalOpenTelemetry
and may not work if you try to manually (re)configure GlobalOpenTelemetry
.