Skip to content

feat: add S3 Bucket and Key params in instrumentation #3274

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

Merged
merged 6 commits into from
Apr 20, 2023
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ Notes:
[float]
===== Features

* Add `aws.s3.bucket` and `aws.s3.key` attributes for OpenTelemetry in S3 instrumentation.
Spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/semantic_conventions/trace/instrumentation/aws-sdk.yml#L435
({issues}3150[#3150]).

[float]
===== Bug fixes

Expand Down
17 changes: 11 additions & 6 deletions lib/instrumentation/generic-span.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,18 @@ GenericSpan.prototype._serializeOTel = function (payload) {
payload.otel = {
span_kind: this._otelKind
}
if (this._otelAttributes) {
// Though the spec allows it ("MAY"), we are opting *not* to report OTel
// span attributes as labels for older (<7.16) versions of APM server.
// This is to avoid the added complexity of guarding allowed attribute
// value types to those supported by the APM server intake API.
payload.otel.attributes = this._otelAttributes
}

if (this._otelAttributes) {
// Though the spec allows it ("MAY"), we are opting *not* to report OTel
// span attributes as labels for older (<7.16) versions of APM server.
// This is to avoid the added complexity of guarding allowed attribute
// value types to those supported by the APM server intake API.
if (!payload.otel) {
payload.otel = {}
}

payload.otel.attributes = this._otelAttributes
}
}

Expand Down
17 changes: 16 additions & 1 deletion lib/instrumentation/modules/aws-sdk/s3.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ function resourceFromBucket (bucket) {
// @param {AWS.Request} request https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html
function instrumentationS3 (orig, origArguments, request, AWS, agent, { version, enabled }) {
const opName = opNameFromOperation(request.operation)
const params = request.params
const bucket = params && params.Bucket
const resource = resourceFromBucket(bucket)
let name = 'S3 ' + opName
const resource = resourceFromBucket(request.params && request.params.Bucket)
if (resource) {
name += ' ' + resource
}
Expand All @@ -65,6 +67,19 @@ function instrumentationS3 (orig, origArguments, request, AWS, agent, { version,
return orig.apply(request, origArguments)
}

// As for now OTel spec defines attributes for operations that require a Bucket
// if that changes we should review this guard
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/semantic_conventions/trace/instrumentation/aws-sdk.yml#L435
if (bucket) {
const otelAttrs = span._getOTelAttributes()

otelAttrs['aws.s3.bucket'] = bucket

if (params.Key) {
otelAttrs['aws.s3.key'] = params.Key
}
}

const onComplete = function (response) {
// `response` is an AWS.Response
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Response.html
Expand Down
51 changes: 51 additions & 0 deletions test/instrumentation/modules/aws-sdk/s3.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 200, response: { encoded_body_size: 177 } }
},
otel: {
attributes: { 'aws.s3.bucket': 'elasticapmtest-bucket-1' }
},
outcome: 'success'
}, 'createTheBucketIfNecessary produced expected span')

Expand All @@ -154,6 +157,9 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 200 }
},
otel: {
attributes: { 'aws.s3.bucket': 'elasticapmtest-bucket-1' }
},
outcome: 'success'
}, 'waitForBucketToExist produced expected span')

Expand All @@ -172,6 +178,12 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 200 }
},
otel: {
attributes: {
'aws.s3.bucket': 'elasticapmtest-bucket-1',
'aws.s3.key': 'aDir/aFile.txt'
}
},
outcome: 'success'
}, 'createObj produced expected span')

Expand All @@ -190,6 +202,12 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 200 }
},
otel: {
attributes: {
'aws.s3.bucket': 'elasticapmtest-bucket-1',
'aws.s3.key': 'aDir/aFile.txt'
}
},
outcome: 'success'
}, 'waitForObjectToExist produced expected span')

Expand All @@ -208,6 +226,12 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 200, response: { encoded_body_size: 8 } }
},
otel: {
attributes: {
'aws.s3.bucket': 'elasticapmtest-bucket-1',
'aws.s3.key': 'aDir/aFile.txt'
}
},
outcome: 'success'
}, 'getObj produced expected span')

Expand All @@ -226,6 +250,12 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 304 }
},
otel: {
attributes: {
'aws.s3.bucket': 'elasticapmtest-bucket-1',
'aws.s3.key': 'aDir/aFile.txt'
}
},
outcome: 'success'
}, 'getObjConditionalGet produced expected span')

Expand All @@ -244,6 +274,12 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 200, response: { encoded_body_size: 8 } }
},
otel: {
attributes: {
'aws.s3.bucket': 'elasticapmtest-bucket-1',
'aws.s3.key': 'aDir/aFile.txt'
}
},
outcome: 'success'
}, 'getObjUsingPromise produced expected span')

Expand All @@ -263,6 +299,12 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 404, response: { encoded_body_size: 207 } }
},
otel: {
attributes: {
'aws.s3.bucket': 'elasticapmtest-bucket-1',
'aws.s3.key': 'aDir/aFile.txt-does-not-exist'
}
},
outcome: 'failure'
}, 'getObjNonExistantObject produced expected span')
t.equal(errors.length, 1, 'got 1 error')
Expand All @@ -285,6 +327,12 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 204 }
},
otel: {
attributes: {
'aws.s3.bucket': 'elasticapmtest-bucket-1',
'aws.s3.key': 'aDir/aFile.txt'
}
},
outcome: 'success'
}, 'deleteTheObj produced expected span')

Expand All @@ -303,6 +351,9 @@ tape.test('simple S3 usage scenario', function (t) {
},
http: { status_code: 204 }
},
otel: {
attributes: { 'aws.s3.bucket': 'elasticapmtest-bucket-1' }
},
outcome: 'success'
}, 'deleteTheBucketIfCreatedIt produced expected span')

Expand Down