Skip to content

Commit 6d77251

Browse files
authored
Merge branch 'main' into openai-inst
2 parents 1cd03cb + cde6d4d commit 6d77251

File tree

8 files changed

+3102
-101
lines changed

8 files changed

+3102
-101
lines changed

examples/express/src/client.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616

1717
import { setupTracing } from './tracer';
1818

19+
// Initialize tracing before importing other moduless
20+
const tracer = setupTracing('example-express-client');
21+
1922
import * as api from '@opentelemetry/api';
2023
import * as axios from 'axios';
2124

22-
const tracer = setupTracing('example-express-client');
23-
2425
async function makeRequest() {
2526
const span = tracer.startSpan('client.makeRequest()', {
2627
kind: api.SpanKind.CLIENT,

examples/express/src/server.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616

1717
import { setupTracing } from './tracer';
1818

19+
// Initialize tracing before importing other modules
20+
setupTracing('example-express-server');
21+
1922
// Require in rest of modules
2023
import * as express from 'express';
2124
import * as axios from 'axios';
2225
import { RequestHandler } from 'express';
2326

24-
setupTracing('example-express-server');
25-
2627
// Setup express
2728
const app = express();
2829
const PORT = 8080;

package-lock.json

Lines changed: 2908 additions & 97 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/instrumentation-aws-sdk/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"@aws-sdk/client-kinesis": "^3.85.0",
6060
"@aws-sdk/client-lambda": "^3.85.0",
6161
"@aws-sdk/client-s3": "^3.85.0",
62+
"@aws-sdk/client-secrets-manager": "^3.85.0",
6263
"@aws-sdk/client-sns": "^3.85.0",
6364
"@aws-sdk/client-sqs": "^3.85.0",
6465
"@aws-sdk/types": "^3.370.0",

packages/instrumentation-aws-sdk/src/semconv.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@ export const GEN_AI_TOKEN_TYPE_VALUE_INPUT = 'input' as const;
159159
*/
160160
export const GEN_AI_TOKEN_TYPE_VALUE_OUTPUT = 'output' as const;
161161

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

packages/instrumentation-aws-sdk/src/services/ServicesExtensions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from '../types';
2424
import { BedrockRuntimeServiceExtension } from './bedrock-runtime';
2525
import { DynamodbServiceExtension } from './dynamodb';
26+
import { SecretsManagerServiceExtension } from './secretsmanager';
2627
import { SnsServiceExtension } from './sns';
2728
import { LambdaServiceExtension } from './lambda';
2829
import { S3ServiceExtension } from './s3';
@@ -32,6 +33,7 @@ export class ServicesExtensions implements ServiceExtension {
3233
services: Map<string, ServiceExtension> = new Map();
3334

3435
constructor() {
36+
this.services.set('SecretsManager', new SecretsManagerServiceExtension());
3537
this.services.set('SQS', new SqsServiceExtension());
3638
this.services.set('SNS', new SnsServiceExtension());
3739
this.services.set('DynamoDB', new DynamodbServiceExtension());
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { Attributes, Span, SpanKind, Tracer } from '@opentelemetry/api';
17+
import { ATTR_AWS_SECRETSMANAGER_SECRET_ARN } from '../semconv';
18+
import { RequestMetadata, ServiceExtension } from './ServiceExtension';
19+
import {
20+
NormalizedRequest,
21+
NormalizedResponse,
22+
AwsSdkInstrumentationConfig,
23+
} from '../types';
24+
25+
export class SecretsManagerServiceExtension implements ServiceExtension {
26+
requestPreSpanHook(
27+
request: NormalizedRequest,
28+
_config: AwsSdkInstrumentationConfig
29+
): RequestMetadata {
30+
const secretId = request.commandInput?.SecretId;
31+
const spanKind: SpanKind = SpanKind.CLIENT;
32+
let spanName: string | undefined;
33+
const spanAttributes: Attributes = {};
34+
if (
35+
typeof secretId === 'string' &&
36+
secretId.startsWith('arn:aws:secretsmanager:')
37+
) {
38+
spanAttributes[ATTR_AWS_SECRETSMANAGER_SECRET_ARN] = secretId;
39+
}
40+
41+
return {
42+
isIncoming: false,
43+
spanAttributes,
44+
spanKind,
45+
spanName,
46+
};
47+
}
48+
49+
responseHook(
50+
response: NormalizedResponse,
51+
span: Span,
52+
tracer: Tracer,
53+
config: AwsSdkInstrumentationConfig
54+
): void {
55+
const secretArn = response.data?.ARN;
56+
if (secretArn) {
57+
span.setAttribute(ATTR_AWS_SECRETSMANAGER_SECRET_ARN, secretArn);
58+
}
59+
}
60+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { getTestSpans } from '@opentelemetry/contrib-test-utils';
18+
import { SpanKind } from '@opentelemetry/api';
19+
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
20+
import { ATTR_AWS_SECRETSMANAGER_SECRET_ARN } from '@opentelemetry/semantic-conventions/incubating';
21+
22+
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
23+
24+
import { expect } from 'expect';
25+
import * as nock from 'nock';
26+
27+
const region = 'us-east-1';
28+
29+
describe('SecretsManager', () => {
30+
let secretsManager: SecretsManager;
31+
beforeEach(() => {
32+
secretsManager = new SecretsManager({
33+
region: region,
34+
credentials: {
35+
accessKeyId: 'abcde',
36+
secretAccessKey: 'abcde',
37+
},
38+
});
39+
});
40+
41+
describe('DescribeSecret', () => {
42+
const testParams = [
43+
'testId',
44+
'badarn:aws:secretsmanager:us-weast-1:123456789123:secret:testId123456',
45+
'arn:aws:secretsmanager:us-east-1:123456789123:secret:testId123456',
46+
];
47+
48+
testParams.forEach(secretId => {
49+
it('should generate secret arn attribute only if secretId is an valid ARN', async () => {
50+
nock(`https://secretsmanager.${region}.amazonaws.com/`)
51+
.post('/')
52+
.reply(200, '{}');
53+
54+
await secretsManager.describeSecret({
55+
SecretId: secretId,
56+
});
57+
58+
const testSpans: ReadableSpan[] = getTestSpans();
59+
const getDescribeSecretSpans: ReadableSpan[] = testSpans.filter(
60+
(s: ReadableSpan) => {
61+
return s.name === 'SecretsManager.DescribeSecret';
62+
}
63+
);
64+
65+
expect(getDescribeSecretSpans.length).toBe(1);
66+
const describeSecretSpan = getDescribeSecretSpans[0];
67+
68+
if (secretId.startsWith('arn:aws:secretsmanager:')) {
69+
expect(
70+
describeSecretSpan.attributes[ATTR_AWS_SECRETSMANAGER_SECRET_ARN]
71+
).toBe(secretId);
72+
} else {
73+
expect(
74+
describeSecretSpan.attributes[ATTR_AWS_SECRETSMANAGER_SECRET_ARN]
75+
).toBeUndefined();
76+
}
77+
78+
expect(describeSecretSpan.kind).toBe(SpanKind.CLIENT);
79+
});
80+
});
81+
});
82+
83+
describe('GetSecretValue', () => {
84+
it('secret arn attribute should be populated from the response', async () => {
85+
const secretIdArn =
86+
'arn:aws:secretsmanager:us-east-1:123456789123:secret:testId123456';
87+
88+
nock(`https://secretsmanager.${region}.amazonaws.com/`)
89+
.post('/')
90+
.reply(200, {
91+
ARN: secretIdArn,
92+
Name: 'testId',
93+
});
94+
95+
await secretsManager.getSecretValue({
96+
SecretId: 'testSecret',
97+
});
98+
99+
const testSpans: ReadableSpan[] = getTestSpans();
100+
const getSecretValueSpans: ReadableSpan[] = testSpans.filter(
101+
(s: ReadableSpan) => {
102+
return s.name === 'SecretsManager.GetSecretValue';
103+
}
104+
);
105+
106+
expect(getSecretValueSpans.length).toBe(1);
107+
108+
const secretValueSpan = getSecretValueSpans[0];
109+
expect(
110+
secretValueSpan.attributes[ATTR_AWS_SECRETSMANAGER_SECRET_ARN]
111+
).toBe(secretIdArn);
112+
expect(secretValueSpan.kind).toBe(SpanKind.CLIENT);
113+
});
114+
});
115+
});

0 commit comments

Comments
 (0)