Skip to content

Commit 4ecf145

Browse files
david-lunaPeterEinberger
authored andcommitted
feat: add S3 Bucket and Key params in instrumentation (elastic#3274)
Extract the parameters Bucket and Key from the instrumented requests to S3. Parameters are added into OTel attributes of the span following the OTel semconv spec Closes: elastic#3150
1 parent 49e10d5 commit 4ecf145

File tree

4 files changed

+82
-7
lines changed

4 files changed

+82
-7
lines changed

CHANGELOG.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ Notes:
4040
[float]
4141
===== Features
4242
43+
* Add `aws.s3.bucket` and `aws.s3.key` attributes for OpenTelemetry in S3 instrumentation.
44+
Spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/semantic_conventions/trace/instrumentation/aws-sdk.yml#L435
45+
({issues}3150[#3150]).
46+
4347
[float]
4448
===== Bug fixes
4549

lib/instrumentation/generic-span.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,18 @@ GenericSpan.prototype._serializeOTel = function (payload) {
246246
payload.otel = {
247247
span_kind: this._otelKind
248248
}
249-
if (this._otelAttributes) {
250-
// Though the spec allows it ("MAY"), we are opting *not* to report OTel
251-
// span attributes as labels for older (<7.16) versions of APM server.
252-
// This is to avoid the added complexity of guarding allowed attribute
253-
// value types to those supported by the APM server intake API.
254-
payload.otel.attributes = this._otelAttributes
249+
}
250+
251+
if (this._otelAttributes) {
252+
// Though the spec allows it ("MAY"), we are opting *not* to report OTel
253+
// span attributes as labels for older (<7.16) versions of APM server.
254+
// This is to avoid the added complexity of guarding allowed attribute
255+
// value types to those supported by the APM server intake API.
256+
if (!payload.otel) {
257+
payload.otel = {}
255258
}
259+
260+
payload.otel.attributes = this._otelAttributes
256261
}
257262
}
258263

lib/instrumentation/modules/aws-sdk/s3.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ function resourceFromBucket (bucket) {
5252
// @param {AWS.Request} request https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html
5353
function instrumentationS3 (orig, origArguments, request, AWS, agent, { version, enabled }) {
5454
const opName = opNameFromOperation(request.operation)
55+
const params = request.params
56+
const bucket = params && params.Bucket
57+
const resource = resourceFromBucket(bucket)
5558
let name = 'S3 ' + opName
56-
const resource = resourceFromBucket(request.params && request.params.Bucket)
5759
if (resource) {
5860
name += ' ' + resource
5961
}
@@ -65,6 +67,19 @@ function instrumentationS3 (orig, origArguments, request, AWS, agent, { version,
6567
return orig.apply(request, origArguments)
6668
}
6769

70+
// As for now OTel spec defines attributes for operations that require a Bucket
71+
// if that changes we should review this guard
72+
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/semantic_conventions/trace/instrumentation/aws-sdk.yml#L435
73+
if (bucket) {
74+
const otelAttrs = span._getOTelAttributes()
75+
76+
otelAttrs['aws.s3.bucket'] = bucket
77+
78+
if (params.Key) {
79+
otelAttrs['aws.s3.key'] = params.Key
80+
}
81+
}
82+
6883
const onComplete = function (response) {
6984
// `response` is an AWS.Response
7085
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Response.html

test/instrumentation/modules/aws-sdk/s3.test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ tape.test('simple S3 usage scenario', function (t) {
141141
},
142142
http: { status_code: 200, response: { encoded_body_size: 177 } }
143143
},
144+
otel: {
145+
attributes: { 'aws.s3.bucket': 'elasticapmtest-bucket-1' }
146+
},
144147
outcome: 'success'
145148
}, 'createTheBucketIfNecessary produced expected span')
146149

@@ -159,6 +162,9 @@ tape.test('simple S3 usage scenario', function (t) {
159162
},
160163
http: { status_code: 200 }
161164
},
165+
otel: {
166+
attributes: { 'aws.s3.bucket': 'elasticapmtest-bucket-1' }
167+
},
162168
outcome: 'success'
163169
}, 'waitForBucketToExist produced expected span')
164170

@@ -177,6 +183,12 @@ tape.test('simple S3 usage scenario', function (t) {
177183
},
178184
http: { status_code: 200 }
179185
},
186+
otel: {
187+
attributes: {
188+
'aws.s3.bucket': 'elasticapmtest-bucket-1',
189+
'aws.s3.key': 'aDir/aFile.txt'
190+
}
191+
},
180192
outcome: 'success'
181193
}, 'createObj produced expected span')
182194

@@ -195,6 +207,12 @@ tape.test('simple S3 usage scenario', function (t) {
195207
},
196208
http: { status_code: 200 }
197209
},
210+
otel: {
211+
attributes: {
212+
'aws.s3.bucket': 'elasticapmtest-bucket-1',
213+
'aws.s3.key': 'aDir/aFile.txt'
214+
}
215+
},
198216
outcome: 'success'
199217
}, 'waitForObjectToExist produced expected span')
200218

@@ -213,6 +231,12 @@ tape.test('simple S3 usage scenario', function (t) {
213231
},
214232
http: { status_code: 200, response: { encoded_body_size: 8 } }
215233
},
234+
otel: {
235+
attributes: {
236+
'aws.s3.bucket': 'elasticapmtest-bucket-1',
237+
'aws.s3.key': 'aDir/aFile.txt'
238+
}
239+
},
216240
outcome: 'success'
217241
}, 'getObj produced expected span')
218242

@@ -231,6 +255,12 @@ tape.test('simple S3 usage scenario', function (t) {
231255
},
232256
http: { status_code: 304 }
233257
},
258+
otel: {
259+
attributes: {
260+
'aws.s3.bucket': 'elasticapmtest-bucket-1',
261+
'aws.s3.key': 'aDir/aFile.txt'
262+
}
263+
},
234264
outcome: 'success'
235265
}, 'getObjConditionalGet produced expected span')
236266

@@ -249,6 +279,12 @@ tape.test('simple S3 usage scenario', function (t) {
249279
},
250280
http: { status_code: 200, response: { encoded_body_size: 8 } }
251281
},
282+
otel: {
283+
attributes: {
284+
'aws.s3.bucket': 'elasticapmtest-bucket-1',
285+
'aws.s3.key': 'aDir/aFile.txt'
286+
}
287+
},
252288
outcome: 'success'
253289
}, 'getObjUsingPromise produced expected span')
254290

@@ -268,6 +304,12 @@ tape.test('simple S3 usage scenario', function (t) {
268304
},
269305
http: { status_code: 404, response: { encoded_body_size: 207 } }
270306
},
307+
otel: {
308+
attributes: {
309+
'aws.s3.bucket': 'elasticapmtest-bucket-1',
310+
'aws.s3.key': 'aDir/aFile.txt-does-not-exist'
311+
}
312+
},
271313
outcome: 'failure'
272314
}, 'getObjNonExistantObject produced expected span')
273315
t.equal(errors.length, 1, 'got 1 error')
@@ -290,6 +332,12 @@ tape.test('simple S3 usage scenario', function (t) {
290332
},
291333
http: { status_code: 204 }
292334
},
335+
otel: {
336+
attributes: {
337+
'aws.s3.bucket': 'elasticapmtest-bucket-1',
338+
'aws.s3.key': 'aDir/aFile.txt'
339+
}
340+
},
293341
outcome: 'success'
294342
}, 'deleteTheObj produced expected span')
295343

@@ -308,6 +356,9 @@ tape.test('simple S3 usage scenario', function (t) {
308356
},
309357
http: { status_code: 204 }
310358
},
359+
otel: {
360+
attributes: { 'aws.s3.bucket': 'elasticapmtest-bucket-1' }
361+
},
311362
outcome: 'success'
312363
}, 'deleteTheBucketIfCreatedIt produced expected span')
313364

0 commit comments

Comments
 (0)