Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow custom aws sdk service extensions via config (#829) #877

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ aws-sdk instrumentation has few options available to choose from. You can set th
| `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)|
| `customServiceExtensions` | `AwsSdkServiceExtensionDefinition[]` | Allows for the provision of custom service extensions by the end user. |

## Span Attributes

Expand Down Expand Up @@ -100,6 +101,8 @@ Specific service logic currently implemented for:
- [SNS](./docs/sns.md)
- DynamoDb

The `customServiceExtensions` configuration parameter allows for users to define their own custom service extensions beyond these built-in ones, or to extend or replace these built-in ones.

## Potential Side Effects

The instrumentation is doing best effort to support the trace specification of OpenTelemetry. For SQS, it involves defining new attributes on the `Messages` array, as well as on the manipulated types generated from this array (to set correct trace context for a single SQS message operation). Those properties are defined as [non-enumerable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties) properties, so they have minimum side effect on the app. They will, however, show when using the `Object.getOwnPropertyDescriptors` and `Reflect.ownKeys` functions on SQS `Messages` array and for each `Message` in the array.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,31 @@
"dependencies": {
"@opentelemetry/core": "^1.0.0",
"@opentelemetry/instrumentation": "^0.27.0",
"@opentelemetry/semantic-conventions": "^1.0.0",
"@opentelemetry/propagation-utils": "^0.26.0"
"@opentelemetry/propagation-utils": "^0.26.0",
"@opentelemetry/semantic-conventions": "^1.0.0"
},
"devDependencies": {
"@aws-sdk/client-dynamodb": "3.37.0",
"@aws-sdk/client-lambda": "^3.50.0",
"@aws-sdk/client-s3": "3.37.0",
"@aws-sdk/client-sqs": "3.37.0",
"@aws-sdk/types": "3.37.0",
"@opentelemetry/api": "1.0.1",
"@opentelemetry/contrib-test-utils": "^0.29.0",
"@opentelemetry/sdk-trace-base": "1.0.1",
"@types/aws-lambda": "^8.10.92",
"@types/mocha": "8.2.3",
"@types/node": "16.11.21",
"@types/sinon": "10.0.6",
"aws-sdk": "2.1008.0",
"eslint": "8.7.0",
"expect": "27.4.2",
"gts": "3.1.0",
"mocha": "7.2.0",
"nock": "13.2.1",
"nyc": "15.1.0",
"rimraf": "3.0.2",
"sinon": "13.0.1",
"gts": "3.1.0",
"@opentelemetry/contrib-test-utils": "^0.29.0",
"test-all-versions": "5.0.1",
"ts-mocha": "8.0.0",
"typescript": "4.3.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,24 @@ type V2PluginRequest = AWS.Request<any, any> & {
export class AwsInstrumentation extends InstrumentationBase<typeof AWS> {
static readonly component = 'aws-sdk';
protected override _config!: AwsSdkInstrumentationConfig;
private servicesExtensions: ServicesExtensions = new ServicesExtensions();
private servicesExtensions: ServicesExtensions;

constructor(config: AwsSdkInstrumentationConfig = {}) {
super(
'@opentelemetry/instrumentation-aws-sdk',
VERSION,
Object.assign({}, config)
);
this.servicesExtensions = new ServicesExtensions(
config.customServiceExtensions
);
}

override setConfig(config: AwsSdkInstrumentationConfig = {}) {
this._config = Object.assign({}, config);
this.servicesExtensions = new ServicesExtensions(
this._config.customServiceExtensions
);
}

protected init(): InstrumentationModuleDefinition<any>[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
*/
export * from './aws-sdk';
export * from './types';
export * from './services';
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ServiceExtension, RequestMetadata } from './ServiceExtension';
import { SqsServiceExtension } from './sqs';
import {
AwsSdkInstrumentationConfig,
AwsSdkServiceExtensionDefinition,
NormalizedRequest,
NormalizedResponse,
} from '../types';
Expand All @@ -27,10 +28,18 @@ import { SnsServiceExtension } from './sns';
export class ServicesExtensions implements ServiceExtension {
services: Map<string, ServiceExtension> = new Map();

constructor() {
constructor(customServiceExtensions?: AwsSdkServiceExtensionDefinition[]) {
this.services.set('SQS', new SqsServiceExtension());
this.services.set('SNS', new SnsServiceExtension());
this.services.set('DynamoDB', new DynamodbServiceExtension());
if (customServiceExtensions !== undefined) {
for (const serviceExtensionDefinition of customServiceExtensions) {
this.services.set(
serviceExtensionDefinition.serviceName,
serviceExtensionDefinition.extension
);
}
}
}

requestPreSpanHook(request: NormalizedRequest): RequestMetadata {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@
* limitations under the License.
*/
export { ServicesExtensions } from './ServicesExtensions';
export { RequestMetadata, ServiceExtension } from './ServiceExtension';
export { SnsServiceExtension } from './sns';
export { SqsServiceExtension } from './sqs';
export { DynamodbServiceExtension } from './dynamodb';
13 changes: 13 additions & 0 deletions plugins/node/opentelemetry-instrumentation-aws-sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { Span } from '@opentelemetry/api';
import { InstrumentationConfig } from '@opentelemetry/instrumentation';
import type * as AWS from 'aws-sdk';
import { ServiceExtension } from './services/ServiceExtension';

/**
* These are normalized request and response, which are used by both sdk v2 and v3.
Expand Down Expand Up @@ -61,6 +62,10 @@ export interface AwsSdkSqsProcessCustomAttributeFunction {
(span: Span, sqsProcessInfo: AwsSdkSqsProcessHookInformation): void;
}

export interface AwsSdkServiceExtensionDefinition {
serviceName: string;
extension: ServiceExtension;
}
export interface AwsSdkInstrumentationConfig extends InstrumentationConfig {
/** hook for adding custom attributes before request is sent to aws */
preRequestHook?: AwsSdkRequestCustomAttributeFunction;
Expand Down Expand Up @@ -91,4 +96,12 @@ export interface AwsSdkInstrumentationConfig extends InstrumentationConfig {
* By default it is off.
*/
sqsExtractContextPropagationFromPayload?: boolean;

/**
* Some users will want to be able to leverage the AWS SDK instrumentation to provide instrumentation
* for services not yet catered for, or to instrument already catered for services in different ways.
* Rather than forcing them to implement an instrumentation from scratch this allows them to specify
* custom services extensions to be used alongside the built in ones
*/
customServiceExtensions?: AwsSdkServiceExtensionDefinition[];
}
Loading