Skip to content

Commit cdbb29f

Browse files
ramesiusblumamir
andauthored
fix(instrumentation-aws-sdk): remove un-sanitised db.statement span attribute from DynamoDB spans (#1748)
* feat: add configuration to customise dynamodb statement serialization * fix: format readme * chore: pass DiagLogger to DynamoDB instrumentation * chore: omit db statement when serializer is not configured or when it returned undefined Allow for passing operation to serializer * chore: run lint:fix --------- Co-authored-by: Amir Blum <[email protected]>
1 parent 86a21d7 commit cdbb29f

File tree

10 files changed

+558
-61
lines changed

10 files changed

+558
-61
lines changed

plugins/node/opentelemetry-instrumentation-aws-sdk/README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ npm install --save @opentelemetry/instrumentation-aws-sdk
2020
For further automatic instrumentation instruction see the [@opentelemetry/instrumentation](https://www.npmjs.com/package/@opentelemetry/instrumentation) package.
2121

2222
```js
23-
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
24-
const { registerInstrumentations } = require("@opentelemetry/instrumentation");
23+
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
24+
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
2525
const {
2626
AwsInstrumentation,
27-
} = require("@opentelemetry/instrumentation-aws-sdk");
27+
} = require('@opentelemetry/instrumentation-aws-sdk');
2828

2929
const provider = new NodeTracerProvider();
3030
provider.register();
@@ -42,13 +42,14 @@ registerInstrumentations({
4242

4343
aws-sdk instrumentation has few options available to choose from. You can set the following:
4444

45-
| Options | Type | Description |
46-
| --------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
47-
| `preRequestHook` | `AwsSdkRequestCustomAttributeFunction` | Hook called before request send, which allow to add custom attributes to span. |
48-
| `responseHook` | `AwsSdkResponseCustomAttributeFunction` | Hook for adding custom attributes when response is received from aws. |
49-
| `sqsProcessHook` | `AwsSdkSqsProcessCustomAttributeFunction` | Hook called after starting sqs `process` span (for each sqs received message), which allow to add custom attributes to it. |
50-
| `suppressInternalInstrumentation` | `boolean` | Most aws operation use http requests under the hood. Set this to `true` to hide all underlying http spans. |
51-
| `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)|
45+
| Options | Type | Description |
46+
| ----------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47+
| `preRequestHook` | `AwsSdkRequestCustomAttributeFunction` | Hook called before request send, which allow to add custom attributes to span. |
48+
| `responseHook` | `AwsSdkResponseCustomAttributeFunction` | Hook for adding custom attributes when response is received from aws. |
49+
| `sqsProcessHook` | `AwsSdkSqsProcessCustomAttributeFunction` | Hook called after starting sqs `process` span (for each sqs received message), which allow to add custom attributes to it. |
50+
| `suppressInternalInstrumentation` | `boolean` | Most aws operation use http requests under the hood. Set this to `true` to hide all underlying http spans. |
51+
| `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) |
52+
| `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`. |
5253

5354
## Span Attributes
5455

@@ -82,8 +83,8 @@ Usage example:
8283
```js
8384
awsInstrumentationConfig = {
8485
preRequestHook: (span, request) => {
85-
if (span.serviceName === "s3") {
86-
span.setAttribute("s3.bucket.name", request.commandInput["Bucket"]);
86+
if (span.serviceName === 's3') {
87+
span.setAttribute('s3.bucket.name', request.commandInput['Bucket']);
8788
}
8889
},
8990
};

plugins/node/opentelemetry-instrumentation-aws-sdk/src/aws-sdk.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -475,8 +475,11 @@ export class AwsInstrumentation extends InstrumentationBase<any> {
475475
command.input,
476476
undefined
477477
);
478-
const requestMetadata =
479-
self.servicesExtensions.requestPreSpanHook(normalizedRequest);
478+
const requestMetadata = self.servicesExtensions.requestPreSpanHook(
479+
normalizedRequest,
480+
self._config,
481+
self._diag
482+
);
480483
const span = self._startAwsV3Span(normalizedRequest, requestMetadata);
481484
const activeContextWithSpan = trace.setSpan(context.active(), span);
482485

@@ -602,8 +605,11 @@ export class AwsInstrumentation extends InstrumentationBase<any> {
602605
}
603606

604607
const normalizedRequest = normalizeV2Request(this);
605-
const requestMetadata =
606-
self.servicesExtensions.requestPreSpanHook(normalizedRequest);
608+
const requestMetadata = self.servicesExtensions.requestPreSpanHook(
609+
normalizedRequest,
610+
self._config,
611+
self._diag
612+
);
607613
const span = self._startAwsV2Span(
608614
this,
609615
requestMetadata,
@@ -642,8 +648,11 @@ export class AwsInstrumentation extends InstrumentationBase<any> {
642648
}
643649

644650
const normalizedRequest = normalizeV2Request(this);
645-
const requestMetadata =
646-
self.servicesExtensions.requestPreSpanHook(normalizedRequest);
651+
const requestMetadata = self.servicesExtensions.requestPreSpanHook(
652+
normalizedRequest,
653+
self._config,
654+
self._diag
655+
);
647656
const span = self._startAwsV2Span(
648657
this,
649658
requestMetadata,

plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/ServiceExtension.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { Span, SpanAttributes, SpanKind, Tracer } from '@opentelemetry/api';
16+
import {
17+
DiagLogger,
18+
Span,
19+
SpanAttributes,
20+
SpanKind,
21+
Tracer,
22+
} from '@opentelemetry/api';
1723
import {
1824
AwsSdkInstrumentationConfig,
1925
NormalizedRequest,
@@ -30,7 +36,11 @@ export interface RequestMetadata {
3036

3137
export interface ServiceExtension {
3238
// called before request is sent, and before span is started
33-
requestPreSpanHook: (request: NormalizedRequest) => RequestMetadata;
39+
requestPreSpanHook: (
40+
request: NormalizedRequest,
41+
config: AwsSdkInstrumentationConfig,
42+
diag: DiagLogger
43+
) => RequestMetadata;
3444

3545
// called before request is sent, and after span is started
3646
requestPostSpanHook?: (request: NormalizedRequest) => void;

plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/ServicesExtensions.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { Tracer, Span } from '@opentelemetry/api';
16+
import { Tracer, Span, DiagLogger } from '@opentelemetry/api';
1717
import { ServiceExtension, RequestMetadata } from './ServiceExtension';
1818
import { SqsServiceExtension } from './sqs';
1919
import {
@@ -35,13 +35,17 @@ export class ServicesExtensions implements ServiceExtension {
3535
this.services.set('Lambda', new LambdaServiceExtension());
3636
}
3737

38-
requestPreSpanHook(request: NormalizedRequest): RequestMetadata {
38+
requestPreSpanHook(
39+
request: NormalizedRequest,
40+
config: AwsSdkInstrumentationConfig,
41+
diag: DiagLogger
42+
): RequestMetadata {
3943
const serviceExtension = this.services.get(request.serviceName);
4044
if (!serviceExtension)
4145
return {
4246
isIncoming: false,
4347
};
44-
return serviceExtension.requestPreSpanHook(request);
48+
return serviceExtension.requestPreSpanHook(request, config, diag);
4549
}
4650

4751
requestPostSpanHook(request: NormalizedRequest) {

plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/dynamodb.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { Span, SpanKind, Tracer } from '@opentelemetry/api';
16+
import { DiagLogger, Span, SpanKind, Tracer } from '@opentelemetry/api';
1717
import { RequestMetadata, ServiceExtension } from './ServiceExtension';
1818
import {
1919
DbSystemValues,
@@ -30,7 +30,11 @@ export class DynamodbServiceExtension implements ServiceExtension {
3030
return Array.isArray(values) ? values : [values];
3131
}
3232

33-
requestPreSpanHook(normalizedRequest: NormalizedRequest): RequestMetadata {
33+
requestPreSpanHook(
34+
normalizedRequest: NormalizedRequest,
35+
config: AwsSdkInstrumentationConfig,
36+
diag: DiagLogger
37+
): RequestMetadata {
3438
const spanKind: SpanKind = SpanKind.CLIENT;
3539
let spanName: string | undefined;
3640
const isIncoming = false;
@@ -40,11 +44,23 @@ export class DynamodbServiceExtension implements ServiceExtension {
4044
[SemanticAttributes.DB_SYSTEM]: DbSystemValues.DYNAMODB,
4145
[SemanticAttributes.DB_NAME]: normalizedRequest.commandInput?.TableName,
4246
[SemanticAttributes.DB_OPERATION]: operation,
43-
[SemanticAttributes.DB_STATEMENT]: JSON.stringify(
44-
normalizedRequest.commandInput
45-
),
4647
};
4748

49+
if (config.dynamoDBStatementSerializer) {
50+
try {
51+
const sanitizedStatement = config.dynamoDBStatementSerializer(
52+
operation,
53+
normalizedRequest.commandInput
54+
);
55+
56+
if (typeof sanitizedStatement === 'string') {
57+
spanAttributes[SemanticAttributes.DB_STATEMENT] = sanitizedStatement;
58+
}
59+
} catch (err) {
60+
diag.error('failed to sanitize DynamoDB statement', err);
61+
}
62+
}
63+
4864
// normalizedRequest.commandInput.RequestItems) is undefined when no table names are returned
4965
// keys in this object are the table names
5066
if (normalizedRequest.commandInput?.TableName) {

plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/lambda.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ class LambdaCommands {
3434
}
3535

3636
export class LambdaServiceExtension implements ServiceExtension {
37-
requestPreSpanHook(request: NormalizedRequest): RequestMetadata {
37+
requestPreSpanHook(
38+
request: NormalizedRequest,
39+
_config: AwsSdkInstrumentationConfig
40+
): RequestMetadata {
3841
const functionName = this.extractFunctionName(request.commandInput);
3942

4043
let spanAttributes: SpanAttributes = {};

plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/sns.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import { injectPropagationContext } from './MessageAttributes';
2727
import { RequestMetadata, ServiceExtension } from './ServiceExtension';
2828

2929
export class SnsServiceExtension implements ServiceExtension {
30-
requestPreSpanHook(request: NormalizedRequest): RequestMetadata {
30+
requestPreSpanHook(
31+
request: NormalizedRequest,
32+
_config: AwsSdkInstrumentationConfig
33+
): RequestMetadata {
3134
let spanKind: SpanKind = SpanKind.CLIENT;
3235
let spanName = `SNS ${request.commandName}`;
3336
const spanAttributes = {

plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/sqs.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ import {
4343
} from './MessageAttributes';
4444

4545
export class SqsServiceExtension implements ServiceExtension {
46-
requestPreSpanHook(request: NormalizedRequest): RequestMetadata {
46+
requestPreSpanHook(
47+
request: NormalizedRequest,
48+
_config: AwsSdkInstrumentationConfig
49+
): RequestMetadata {
4750
const queueUrl = this.extractQueueUrl(request.commandInput);
4851
const queueName = this.extractQueueNameFromUrl(queueUrl);
4952
let spanKind: SpanKind = SpanKind.CLIENT;

plugins/node/opentelemetry-instrumentation-aws-sdk/src/types.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { Span } from '@opentelemetry/api';
1717
import { InstrumentationConfig } from '@opentelemetry/instrumentation';
1818
import { SQS } from './aws-sdk.types';
1919

20+
export type CommandInput = Record<string, any>;
21+
2022
/**
2123
* These are normalized request and response, which are used by both sdk v2 and v3.
2224
* They organize the relevant data in one interface which can be processed in a
@@ -25,7 +27,7 @@ import { SQS } from './aws-sdk.types';
2527
export interface NormalizedRequest {
2628
serviceName: string;
2729
commandName: string;
28-
commandInput: Record<string, any>;
30+
commandInput: CommandInput;
2931
region?: string;
3032
}
3133
export interface NormalizedResponse {
@@ -62,6 +64,11 @@ export interface AwsSdkSqsProcessCustomAttributeFunction {
6264
(span: Span, sqsProcessInfo: AwsSdkSqsProcessHookInformation): void;
6365
}
6466

67+
export type AwsSdkDynamoDBStatementSerializer = (
68+
operation: string,
69+
commandInput: CommandInput
70+
) => string | undefined;
71+
6572
export interface AwsSdkInstrumentationConfig extends InstrumentationConfig {
6673
/** hook for adding custom attributes before request is sent to aws */
6774
preRequestHook?: AwsSdkRequestCustomAttributeFunction;
@@ -72,6 +79,9 @@ export interface AwsSdkInstrumentationConfig extends InstrumentationConfig {
7279
/** hook for adding custom attribute when an sqs process span is started */
7380
sqsProcessHook?: AwsSdkSqsProcessCustomAttributeFunction;
7481

82+
/** custom serializer function for the db.statement attribute in DynamoDB spans */
83+
dynamoDBStatementSerializer?: AwsSdkDynamoDBStatementSerializer;
84+
7585
/**
7686
* Most aws operation use http request under the hood.
7787
* if http instrumentation is enabled, each aws operation will also create

0 commit comments

Comments
 (0)