Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
3447c0e
sqs: use links instead of process spans
seemk Jul 17, 2024
c9bf62a
add tests
seemk Jul 18, 2024
0021486
update readme
seemk Jul 18, 2024
6dfe182
Merge branch 'main' into sqs-batch-receive
seemk Jul 23, 2024
b339cc3
Merge branch 'main' into sqs-batch-receive
seemk Jul 23, 2024
f4d95b0
Merge branch 'main' into sqs-batch-receive
seemk Jul 31, 2024
9581ae6
Merge branch 'main' into sqs-batch-receive
seemk Aug 7, 2024
10ee8c1
Merge branch 'main' into sqs-batch-receive
seemk Aug 14, 2024
940b6cf
Merge branch 'main' into sqs-batch-receive
seemk Aug 20, 2024
0a24be7
Merge branch 'main' into sqs-batch-receive
seemk Aug 22, 2024
e9eeabd
Merge branch 'main' into sqs-batch-receive
seemk Aug 22, 2024
b77188d
Merge branch 'main' into sqs-batch-receive
seemk Aug 27, 2024
4822b0b
Merge branch 'main' into sqs-batch-receive
seemk Aug 27, 2024
5fb4345
Merge branch 'main' into sqs-batch-receive
seemk Sep 10, 2024
25ea56d
Merge branch 'main' into sqs-batch-receive
seemk Sep 11, 2024
35adc39
Merge branch 'main' into sqs-batch-receive
seemk Sep 14, 2024
c68d15e
Merge branch 'main' into sqs-batch-receive
seemk Sep 25, 2024
a9c8df6
Merge branch 'main' into sqs-batch-receive
seemk Oct 2, 2024
6d30fa6
Merge branch 'main' into sqs-batch-receive
seemk Oct 15, 2024
ca771f5
Merge branch 'main' into sqs-batch-receive
seemk Oct 17, 2024
b562d80
Merge branch 'main' into sqs-batch-receive
seemk Oct 30, 2024
a5be283
Merge branch 'main' into sqs-batch-receive
seemk Nov 6, 2024
f237133
Merge branch 'main' into sqs-batch-receive
seemk Nov 7, 2024
62bf036
Merge branch 'main' into sqs-batch-receive
seemk Nov 20, 2024
953b3c5
Merge branch 'main' into sqs-batch-receive
seemk Nov 25, 2024
680f7d2
Merge branch 'main' into sqs-batch-receive
seemk Nov 25, 2024
6b7cb26
Merge branch 'main' into sqs-batch-receive
seemk Nov 26, 2024
0db74dd
Merge branch 'main' into sqs-batch-receive
seemk Nov 27, 2024
454fbf2
Merge branch 'main' into sqs-batch-receive
seemk Dec 2, 2024
f1cfbee
Merge branch 'main' into sqs-batch-receive
seemk Feb 6, 2025
fc5ba3c
Merge branch 'main' into sqs-batch-receive
seemk Mar 14, 2025
8028d85
update semconv values
seemk Mar 14, 2025
4ce0bfb
add tests for v3
seemk Mar 14, 2025
fd7a219
update sqs docs
seemk Mar 14, 2025
c9bcc5a
Merge branch 'main' into sqs-batch-receive
seemk Mar 15, 2025
c70fb0e
remove unused dependency
seemk Mar 15, 2025
cefd08a
Merge branch 'main' into sqs-batch-receive
seemk Mar 18, 2025
33df93b
Merge branch 'main' into sqs-batch-receive
seemk Jun 30, 2025
19e5cdd
remove obsolete test file and exports
seemk Jun 30, 2025
65d17d4
Merge branch 'main' into sqs-batch-receive
seemk Jun 30, 2025
ba514c4
mark semconv values as const
seemk Jun 30, 2025
ce50e2a
Merge branch 'main' into sqs-batch-receive
seemk Jul 24, 2025
94561e4
remove unused dep
seemk Jul 24, 2025
2ae3ff2
ci
seemk Jul 25, 2025
4308ee1
Merge branch 'main' into sqs-batch-receive
trentm Aug 26, 2025
5d50710
regenerate src/semconv.ts with scripts/gen-semconv-ts.js tool
trentm Aug 26, 2025
d18c87e
fix missed conflict in earlier merge
trentm Aug 26, 2025
943ef18
Merge branch 'main' into sqs-batch-receive
trentm Aug 26, 2025
05104ff
regenerate src/semconv.ts again (re-ordering and comments)
trentm Aug 26, 2025
8c2c582
Merge branch 'main' into sqs-batch-receive
pichlermarc Aug 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion packages/instrumentation-aws-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ aws-sdk instrumentation has few options available to choose from. You can set th
| `preRequestHook` | `AwsSdkRequestCustomAttributeFunction` | Hook called before request send, which allow to add custom attributes to span. |
| `responseHook` | `AwsSdkResponseCustomAttributeFunction` | Hook for adding custom attributes when response is received from aws. |
| `exceptionHook` | `AwsSdkExceptionCustomAttributeFunction` | Hook for adding custom attributes when exception is received from aws. |
| `sqsProcessHook` | `AwsSdkSqsProcessCustomAttributeFunction` | Hook called after starting sqs `process` span (for each sqs received message), which allow to add custom attributes to it. |
| `suppressInternalInstrumentation` | `boolean` | Most aws operation use http requests under the hood. Set this to `true` to hide all underlying http spans. |
| `sqsExtractContextPropagationFromPayload` | `boolean` | Will parse and extract context propagation headers from SQS Payload, false by default. [When should it be used?](./doc/sns.md#integration-with-sqs) |
| `dynamoDBStatementSerializer` | `AwsSdkDynamoDBStatementSerializer` | AWS SDK instrumentation will serialize DynamoDB commands to the `db.statement` attribute using the specified function. Defaults to using a serializer that returns `undefined`. |
Expand Down
59 changes: 5 additions & 54 deletions packages/instrumentation-aws-sdk/doc/sqs.md
Original file line number Diff line number Diff line change
@@ -1,68 +1,19 @@
# SQS

SQS is amazon's managed message queue. Thus, it should follow the [OpenTelemetry specification for Messaging systems](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md).
SQS is Amazon's managed message queue. Thus, it should follow the [OpenTelemetry specification for Messaging systems](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/).

## Specific trace semantic

The following methods are automatically enhanced:

### sendMessage / sendMessageBatch

- [Messaging Attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md#messaging-attributes) are added by this instrumentation according to the spec.
- [Messaging Attributes](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/#messaging-attributes) are added by this instrumentation according to the spec.
- OpenTelemetry trace context is injected as SQS MessageAttributes, so the service receiving the message can link cascading spans to the trace which created the message.

### receiveMessage

- [Messaging Attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md#messaging-attributes) are added by this instrumentation according to the spec.
- Additional "processing spans" are created for each message received by the application.
If an application invoked `receiveMessage`, and received a 10 messages batch, a single `messaging.operation` = `receive` span will be created for the `receiveMessage` operation, and 10 `messaging.operation` = `process` spans will be created, one for each message.
Those processing spans are created by the library. This behavior is partially implemented, [See discussion below](#processing-spans).
- Sets the inter process context correctly, so that additional spans created through the process will be linked to parent spans correctly.
This behavior is partially implemented, [See discussion below](#processing-spans).
- [Messaging Attributes](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/#messaging-attributes) are added by this instrumentation according to the spec.
- Sets the inter process context correctly, so that additional spans created through the process will be linked to parent spans correctly.
When multiple messages are received, the instrumentation will attach spank links to the receiving span containing the trace context and message ID of each message.
- Extract trace context from SQS MessageAttributes, and set span's `parent` and `links` correctly according to the spec.

#### Processing Spans

See GH issue [here](https://github.com/open-telemetry/opentelemetry-js-contrib/issues/707)

According to OpenTelemetry specification (and to reasonable expectation for trace structure), user of this library would expect to see one span for the operation of receiving messages batch from SQS, and then, **for each message**, a span with it's own sub-tree for the processing of this specific message.

For example, if a `receiveMessages` returned 2 messages:

- `msg1` resulting in storing something to a DB.
- `msg2` resulting in calling an external HTTP endpoint.

This will result in a creating a DB span that would be the child of `msg1` process span, and an HTTP span that would be the child of `msg2` process span (in opposed to mixing all those operations under the single `receive` span, or start a new trace for each of them).

Unfortunately, this is not so easy to implement in JS:

1. The SDK is calling a single callback for the messages batch, and it's not straightforward to understand when each individual message processing starts and ends (and set the context correctly for cascading spans).
2. If async/await is used, context can be lost when returning data from async functions, for example:

```js
async function asyncRecv() {
const data = await sqs.receiveMessage(recvParams).promise();
// context of receiveMessage is set here
return data;
}

async function poll() {
const result = await asyncRecv();
// context is lost when asyncRecv returns. following spans are created with root context.
await Promise.all(
result.Messages.map((message) => this.processMessage(message))
);
}
```

Current implementation partially solves this issue by patching the `map` \ `forEach` \ `Filter` functions on the `Messages` array of `receiveMessage` result. This handles issues like the one above, but will not handle situations where the processing is done in other patterns (multiple map\forEach calls, index access to the array, other array operations, etc). This is currently an open issue in the instrumentation.

User can add custom attributes to the `process` span, by setting a function to `sqsProcessHook` in instrumentation config. For example:

```js
awsInstrumentationConfig = {
sqsProcessHook: (span, message) => {
span.setAttribute("sqs.receipt_handle", message.params?.ReceiptHandle);
},
};
```
1 change: 0 additions & 1 deletion packages/instrumentation-aws-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"dependencies": {
"@opentelemetry/core": "^2.0.0",
"@opentelemetry/instrumentation": "^0.203.0",
"@opentelemetry/propagation-utils": "^0.31.3",
"@opentelemetry/semantic-conventions": "^1.34.0"
},
"devDependencies": {
Expand Down
2 changes: 0 additions & 2 deletions packages/instrumentation-aws-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ export type {
AwsSdkRequestHookInformation,
AwsSdkResponseCustomAttributeFunction,
AwsSdkResponseHookInformation,
AwsSdkSqsProcessCustomAttributeFunction,
AwsSdkSqsProcessHookInformation,
CommandInput,
NormalizedRequest,
NormalizedResponse,
Expand Down
101 changes: 81 additions & 20 deletions packages/instrumentation-aws-sdk/src/semconv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@
* @see https://github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions#unstable-semconv
*/

/**
* The ARN of the Secret stored in the Secrets Mangger
*
* @example arn:aws:secretsmanager:us-east-1:123456789012:secret:SecretName-6RandomCharacters
*
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const ATTR_AWS_SECRETSMANAGER_SECRET_ARN =
'aws.secretsmanager.secret.arn' as const;

/**
* The ARN of the AWS SNS Topic. An Amazon SNS [topic](https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html) is a logical access point that acts as a communication channel.
*
* @example arn:aws:sns:us-east-1:123456789012:mystack-mytopic-NZJ5JSMVGFIE
*
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const ATTR_AWS_SNS_TOPIC_ARN = 'aws.sns.topic.arn' as const;

/**
* The name of the operation being performed.
*
Expand Down Expand Up @@ -139,42 +158,84 @@ export const ATTR_GEN_AI_USAGE_INPUT_TOKENS =
export const ATTR_GEN_AI_USAGE_OUTPUT_TOKENS =
'gen_ai.usage.output_tokens' as const;

/**
* The number of messages sent, received, or processed in the scope of the batching operation.
*
* @example 0
* @example 1
* @example 2
*
* @note Instrumentations **SHOULD NOT** set `messaging.batch.message_count` on spans that operate with a single message. When a messaging client library supports both batch and single-message API for the same operation, instrumentations **SHOULD** use `messaging.batch.message_count` for batching APIs and **SHOULD NOT** use it for single-message APIs.
*
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const ATTR_MESSAGING_BATCH_MESSAGE_COUNT =
'messaging.batch.message_count' as const;

/**
* The message destination name
*
* @example MyQueue
* @example MyTopic
*
* @note Destination name **SHOULD** uniquely identify a specific queue, topic or other entity within the broker. If
* the broker doesn't have such notion, the destination name **SHOULD** uniquely identify the broker.
*
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const ATTR_MESSAGING_DESTINATION_NAME =
'messaging.destination.name' as const;

/**
* A value used by the messaging system as an identifier for the message, represented as a string.
*
* @example "452a7c7c7c7048c2f887f61572b18fc2"
*
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const ATTR_MESSAGING_MESSAGE_ID = 'messaging.message.id' as const;

/**
* A string identifying the type of the messaging operation.
*
* @note If a custom value is used, it **MUST** be of low cardinality.
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const ATTR_MESSAGING_OPERATION_TYPE =
'messaging.operation.type' as const;

/**
* Enum value "chat" for attribute {@link ATTR_GEN_AI_OPERATION_NAME}.
*
* Chat completion operation such as [OpenAI Chat API](https://platform.openai.com/docs/api-reference/chat)
*
* @experimental This enum value is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const GEN_AI_OPERATION_NAME_VALUE_CHAT = 'chat' as const;

/**
* Enum value "aws.bedrock" for attribute {@link ATTR_GEN_AI_SYSTEM}.
*
* AWS Bedrock
*
* @experimental This enum value is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const GEN_AI_SYSTEM_VALUE_AWS_BEDROCK = 'aws.bedrock' as const;

/**
* Enum value "input" for attribute {@link ATTR_GEN_AI_TOKEN_TYPE}.
*
* Input tokens (prompt, input, etc.)
*
* @experimental This enum value is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const GEN_AI_TOKEN_TYPE_VALUE_INPUT = 'input' as const;

/**
* Enum value "output" for attribute {@link ATTR_GEN_AI_TOKEN_TYPE}.
*
* Output tokens (completion, response, etc.)
*
* @experimental This enum value is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const GEN_AI_TOKEN_TYPE_VALUE_OUTPUT = 'output' as const;

/**
* Originally from '@opentelemetry/semantic-conventions/incubating'
* https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/aws.md#amazon-secrets-manager-attributes
* The ARN of the Secret stored in the Secrets Mangger
* @example arn:aws:secretsmanager:us-east-1:123456789012:secret:SecretName-6RandomCharacters
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const ATTR_AWS_SECRETSMANAGER_SECRET_ARN =
'aws.secretsmanager.secret.arn' as const;

/**
* Originally from '@opentelemetry/semantic-conventions/incubating'
* https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/aws.md#amazon-sns-attributes
* The ARN of the AWS SNS Topic. An Amazon SNS [topic](https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html)
* is a logical access point that acts as a communication channel.
* @example arn:aws:sns:us-east-1:123456789012:mystack-mytopic-NZJ5JSMVGFIE
* @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
*/
export const ATTR_AWS_SNS_TOPIC_ARN = 'aws.sns.topic.arn' as const;
94 changes: 35 additions & 59 deletions packages/instrumentation-aws-sdk/src/services/sqs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ import {
Span,
propagation,
trace,
context,
ROOT_CONTEXT,
Attributes,
} from '@opentelemetry/api';
import { pubsubPropagation } from '@opentelemetry/propagation-utils';
import { RequestMetadata, ServiceExtension } from './ServiceExtension';
import type { SQS } from '../aws-sdk.types';
import {
Expand All @@ -32,16 +30,15 @@ import {
NormalizedResponse,
} from '../types';
import {
MESSAGINGDESTINATIONKINDVALUES_QUEUE,
MESSAGINGOPERATIONVALUES_PROCESS,
MESSAGINGOPERATIONVALUES_RECEIVE,
SEMATTRS_MESSAGING_DESTINATION,
SEMATTRS_MESSAGING_DESTINATION_KIND,
SEMATTRS_MESSAGING_MESSAGE_ID,
SEMATTRS_MESSAGING_OPERATION,
ATTR_URL_FULL,
SEMATTRS_MESSAGING_SYSTEM,
SEMATTRS_MESSAGING_URL,
} from '@opentelemetry/semantic-conventions';
import {
ATTR_MESSAGING_BATCH_MESSAGE_COUNT,
ATTR_MESSAGING_DESTINATION_NAME,
ATTR_MESSAGING_MESSAGE_ID,
ATTR_MESSAGING_OPERATION_TYPE,
} from '../semconv';
import {
contextGetter,
extractPropagationContext,
Expand All @@ -60,11 +57,9 @@ export class SqsServiceExtension implements ServiceExtension {
let spanName: string | undefined;

const spanAttributes: Attributes = {
[SEMATTRS_MESSAGING_SYSTEM]: 'aws.sqs',
[SEMATTRS_MESSAGING_DESTINATION_KIND]:
MESSAGINGDESTINATIONKINDVALUES_QUEUE,
[SEMATTRS_MESSAGING_DESTINATION]: queueName,
[SEMATTRS_MESSAGING_URL]: queueUrl,
[SEMATTRS_MESSAGING_SYSTEM]: 'aws_sqs',
[ATTR_MESSAGING_DESTINATION_NAME]: queueName,
[ATTR_URL_FULL]: queueUrl,
};

let isIncoming = false;
Expand All @@ -75,8 +70,7 @@ export class SqsServiceExtension implements ServiceExtension {
isIncoming = true;
spanKind = SpanKind.CONSUMER;
spanName = `${queueName} receive`;
spanAttributes[SEMATTRS_MESSAGING_OPERATION] =
MESSAGINGOPERATIONVALUES_RECEIVE;
spanAttributes[ATTR_MESSAGING_OPERATION_TYPE] = 'receive';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: There are enum value exports from the unstable semantic-conventions exports that could be used for 'receive' here (and aws_sqs above):

8674:export const MESSAGING_OPERATION_TYPE_VALUE_CREATE = "create" as const;
8685:export const MESSAGING_OPERATION_TYPE_VALUE_DELIVER = "deliver" as const;
8694:export const MESSAGING_OPERATION_TYPE_VALUE_PROCESS = "process" as const;
8705:export const MESSAGING_OPERATION_TYPE_VALUE_PUBLISH = "publish" as const;
8714:export const MESSAGING_OPERATION_TYPE_VALUE_RECEIVE = "receive" as const;
8723:export const MESSAGING_OPERATION_TYPE_VALUE_SEND = "send" as const;
8732:export const MESSAGING_OPERATION_TYPE_VALUE_SETTLE = "settle" as const;

However, this is very minor and could be done in a follow-up PR.


request.commandInput.MessageAttributeNames =
addPropagationFieldsToAttributeNames(
Expand Down Expand Up @@ -136,61 +130,43 @@ export class SqsServiceExtension implements ServiceExtension {
responseHook = (
response: NormalizedResponse,
span: Span,
tracer: Tracer,
_tracer: Tracer,
config: AwsSdkInstrumentationConfig
) => {
switch (response.request.commandName) {
case 'SendMessage':
span.setAttribute(
SEMATTRS_MESSAGING_MESSAGE_ID,
response?.data?.MessageId
);
span.setAttribute(ATTR_MESSAGING_MESSAGE_ID, response?.data?.MessageId);
break;

case 'SendMessageBatch':
// TODO: How should this be handled?
break;

case 'ReceiveMessage': {
const messages: SQS.Message[] = response?.data?.Messages;
if (messages) {
const queueUrl = this.extractQueueUrl(response.request.commandInput);
const queueName = this.extractQueueNameFromUrl(queueUrl);

pubsubPropagation.patchMessagesArrayToStartProcessSpans<SQS.Message>({
messages,
parentContext: trace.setSpan(context.active(), span),
tracer,
messageToSpanDetails: (message: SQS.Message) => ({
name: queueName ?? 'unknown',
parentContext: propagation.extract(
ROOT_CONTEXT,
extractPropagationContext(
message,
config.sqsExtractContextPropagationFromPayload
),
contextGetter
),
const messages: SQS.Message[] = response?.data?.Messages || [];

span.setAttribute(ATTR_MESSAGING_BATCH_MESSAGE_COUNT, messages.length);

for (const message of messages) {
const propagatedContext = propagation.extract(
ROOT_CONTEXT,
extractPropagationContext(
message,
config.sqsExtractContextPropagationFromPayload
),
contextGetter
);

const spanContext = trace.getSpanContext(propagatedContext);

if (spanContext) {
span.addLink({
context: spanContext,
attributes: {
[SEMATTRS_MESSAGING_SYSTEM]: 'aws.sqs',
[SEMATTRS_MESSAGING_DESTINATION]: queueName,
[SEMATTRS_MESSAGING_DESTINATION_KIND]:
MESSAGINGDESTINATIONKINDVALUES_QUEUE,
[SEMATTRS_MESSAGING_MESSAGE_ID]: message.MessageId,
[SEMATTRS_MESSAGING_URL]: queueUrl,
[SEMATTRS_MESSAGING_OPERATION]:
MESSAGINGOPERATIONVALUES_PROCESS,
[ATTR_MESSAGING_MESSAGE_ID]: message.MessageId,
},
}),
processHook: (span: Span, message: SQS.Message) =>
config.sqsProcessHook?.(span, { message }),
});

pubsubPropagation.patchArrayForProcessSpans(
messages,
tracer,
context.active()
);
});
}
}
break;
}
Expand Down
Loading
Loading