Skip to content

Commit

Permalink
feat(aws-sdk): add exception hook
Browse files Browse the repository at this point in the history
  • Loading branch information
project0 committed Dec 5, 2024
1 parent 5d60689 commit 8fe1574
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ 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) |
Expand Down
29 changes: 29 additions & 0 deletions plugins/node/opentelemetry-instrumentation-aws-sdk/src/aws-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,30 @@ export class AwsInstrumentation extends InstrumentationBase<AwsSdkInstrumentatio
);
}

private _callUserExceptionResponseHook(
span: Span,
request: NormalizedRequest,
err: any
) {
const { exceptionHook } = this.getConfig();
if (!exceptionHook) return;
const requestInfo: AwsSdkRequestHookInformation = {
request,
};

safeExecuteInTheMiddle(
() => exceptionHook(span, requestInfo, err),
(e: Error | undefined) => {
if (e)
diag.error(

Check warning on line 328 in plugins/node/opentelemetry-instrumentation-aws-sdk/src/aws-sdk.ts

View check run for this annotation

Codecov / codecov/patch

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

Added line #L328 was not covered by tests
`${AwsInstrumentation.component} instrumentation: exceptionHook error`,
e
);
},
true
);
}

private _registerV2CompletedEvent(
span: Span,
v2Request: V2PluginRequest,
Expand Down Expand Up @@ -557,6 +581,11 @@ export class AwsInstrumentation extends InstrumentationBase<AwsSdkInstrumentatio
message: err.message,
});
span.recordException(err);
self._callUserExceptionResponseHook(
span,
normalizedRequest,
err
);
throw err;
})
.finally(() => {
Expand Down
15 changes: 15 additions & 0 deletions plugins/node/opentelemetry-instrumentation-aws-sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface AwsSdkResponseHookInformation {
moduleVersion?: string;
response: NormalizedResponse;
}

/**
* span can be used to add custom attributes, or for any other need.
* response is the object that is returned to the user calling the aws-sdk operation.
Expand All @@ -57,6 +58,14 @@ export interface AwsSdkResponseCustomAttributeFunction {
(span: Span, responseInfo: AwsSdkResponseHookInformation): void;
}

/**
* span can be used to modify the status.
* As we have no response object, the request can be used to determine the failed aws-sdk operation.
*/
export interface AwsSdkExceptionCustomAttributeFunction {
(span: Span, requestInfo: AwsSdkRequestHookInformation, err: any): void;
}

export interface AwsSdkSqsProcessHookInformation {
message: SQS.Message;
}
Expand All @@ -76,6 +85,12 @@ export interface AwsSdkInstrumentationConfig extends InstrumentationConfig {
/** hook for adding custom attributes when response is received from aws */
responseHook?: AwsSdkResponseCustomAttributeFunction;

/**
* Hook for adding custom attributes when exception is received from aws.
* This hook is only available with aws sdk v3
*/
exceptionHook?: AwsSdkExceptionCustomAttributeFunction;

/** hook for adding custom attribute when an sqs process span is started */
sqsProcessHook?: AwsSdkSqsProcessCustomAttributeFunction;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,50 @@ describe('instrumentation-aws-sdk-v3', () => {
);
});
});

it('handle aws sdk exception', async () => {
instrumentation.disable();
instrumentation.setConfig({
exceptionHook: (
span: Span,
requestInfo: AwsSdkRequestHookInformation,
err: any
) => {
span.setAttribute(
'attribute.from.exception.hook',
requestInfo.request.commandInput.Bucket
);
span.setAttribute('error.from.exception.hook', err.name);
},

suppressInternalInstrumentation: true,
});
instrumentation.enable();

nock(`https://ot-demo-test.s3.${region}.amazonaws.com/`)
.put('/aws-ot-s3-test-object.txt?x-id=PutObject')
.reply(
404,
fs.readFileSync('./test/mock-responses/s3-put-object.xml', 'utf8')
);

const params = {
Bucket: 'ot-demo-test',
Key: 'aws-ot-s3-test-object.txt',
};
try {
await s3Client.putObject(params);
} catch {
expect(getTestSpans().length).toBe(1);
const [span] = getTestSpans();
expect(span.attributes['attribute.from.exception.hook']).toEqual(
params.Bucket
);
expect(span.attributes['error.from.exception.hook']).toEqual(
'NotFound'
);
}
});
});

describe('custom service behavior', () => {
Expand Down

0 comments on commit 8fe1574

Please sign in to comment.