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: implment awk-sdk v2 dynamodb instrumentation #2128

Merged
merged 18 commits into from
Jul 1, 2021
5 changes: 3 additions & 2 deletions .tav.yml
Original file line number Diff line number Diff line change
Expand Up @@ -413,13 +413,14 @@ body-parser:
- node test/sanitize-field-names/express.js

aws-sdk:
# We want this version range:
# We want a version range something like this:
# versions: '>=2.858 <3'
# However, awk-sdk releases *very* frequently (almost every day) and there
# is no need to test *all* those releases. Instead we statically list every
# N=5 releases to test.
#
# Maintenance note: This should be updated periodically.
versions: '2.858.0 || 2.863.0 || 2.868.0 || 2.873.0 || 2.878.0 || 2.883.0 || 2.888.0 || 2.893.0 || 2.898.0 || 2.903.0 || 2.908.0 || 2.913.0 || 2.918.0 || >2.918 <3'
versions: '2.876.0 || 2.881.0 || 2.886.0 || 2.891.0 || 2.896.0 || 2.901.0 || 2.906.0 || 2.911.0 || 2.916.0 || 2.921.0 || 2.926.0 || 2.931.0 || 2.936.0 || >2.918 <3'
astorm marked this conversation as resolved.
Show resolved Hide resolved
commands:
- node test/instrumentation/modules/aws-sdk/sqs.js
- node test/instrumentation/modules/aws-sdk/dynamodb.js
astorm marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ Notes:
* Add <<disable-send, `disableSend`>> configuration option. This supports some
use cases using the APM agent **without** an APM server. ({issues}2101[#2101])

* Add instrumentation of all DynamoDB methods when using the
https://www.npmjs.com/package/aws-sdk[JavaScript AWS SDK v2] (`aws-sdk`).

[float]
===== Bug fixes

Expand Down
2 changes: 1 addition & 1 deletion docs/supported-technologies.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ The Node.js agent will automatically instrument the following modules to give yo
[options="header"]
|=======================================================================
|Module |Version |Note
|https://www.npmjs.com/package/aws-sdk[aws-sdk] |>1 <3 |Will instrument SQS send/receive/delete messages, all S3 methods
|https://www.npmjs.com/package/aws-sdk[aws-sdk] |>1 <3 |Will instrument SQS send/receive/delete messages, all S3 methods, and all DynamoDB methods
|https://www.npmjs.com/package/cassandra-driver[cassandra-driver] |>=3.0.0 |Will instrument all queries
|https://www.npmjs.com/package/elasticsearch[elasticsearch] |>=8.0.0 |Will instrument all queries
|https://www.npmjs.com/package/@elastic/elasticsearch[@elastic/elasticsearch] |>=7.0.0 <8.0.0 |Will instrument all queries
Expand Down
4 changes: 3 additions & 1 deletion lib/instrumentation/modules/aws-sdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ const semver = require('semver')
const shimmer = require('../shimmer')
const { instrumentationS3 } = require('./aws-sdk/s3')
const { instrumentationSqs } = require('./aws-sdk/sqs')
const { instrumentationDynamoDb } = require('./aws-sdk/dynamodb.js')

const instrumentorFromSvcId = {
s3: instrumentationS3,
sqs: instrumentationSqs
sqs: instrumentationSqs,
dynamodb: instrumentationDynamoDb
}

// Called in place of AWS.Request.send and AWS.Request.promise
Expand Down
122 changes: 122 additions & 0 deletions lib/instrumentation/modules/aws-sdk/dynamodb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
'use strict'
const constants = require('../../../constants')
const TYPE = 'db'
const SUBTYPE = 'dynamodb'
const ACTION = 'query'

function getRegionFromRequest (request) {
return request && request.service &&
request.service.config && request.service.config.region
}

function getPortFromRequest (request) {
return request && request.service &&
request.service.endpoint && request.service.endpoint.port
}

function getMethodFromRequest (request) {
const method = request && request.operation
if (method) {
return method[0].toUpperCase() + method.slice(1)
}
}

function getStatementFromRequest (request) {
const method = getMethodFromRequest(request)
if (method === 'Query' && request && request.params && request.params.KeyConditionExpression) {
return request.params.KeyConditionExpression
}
return undefined
}

function getAddressFromRequest (request) {
return request && request.service && request.service.endpoint &&
request.service.endpoint.host
astorm marked this conversation as resolved.
Show resolved Hide resolved
}

function getTableFromRequest (request) {
const table = request && request.params && request.params.TableName
astorm marked this conversation as resolved.
Show resolved Hide resolved
if (!table) {
return ''
}
return ` ${table}`
}

// Creates the span name from request information
function getSpanNameFromRequest (request) {
const method = getMethodFromRequest(request)
const table = getTableFromRequest(request)
const name = `DynamoDB ${method}${table}`
return name
}

function shouldIgnoreRequest (request, agent) {
return false
}

// Main entrypoint for SQS instrumentation
//
// Must call (or one of its function calls must call) the
// `orig` function/method
function instrumentationDynamoDb (orig, origArguments, request, AWS, agent, { version, enabled }) {
if (shouldIgnoreRequest(request, agent)) {
return orig.apply(request, origArguments)
}

const type = TYPE
const subtype = SUBTYPE
const action = ACTION
// const action = getActionFromRequest(request)
astorm marked this conversation as resolved.
Show resolved Hide resolved
const name = getSpanNameFromRequest(request)
const span = agent.startSpan(name, type, subtype, action)
if (!span) {
return orig.apply(request, origArguments)
}
// span.setDestinationContext(getMessageDestinationContextFromRequest(request))
// span.setMessageContext(getMessageContextFromRequest(request))
astorm marked this conversation as resolved.
Show resolved Hide resolved
span.setDbContext({
instance: getRegionFromRequest(request),
statement: getStatementFromRequest(request),
type: SUBTYPE
})
span.setDestinationContext({
address: getAddressFromRequest(request),
port: getPortFromRequest(request),
service: {
name: SUBTYPE,
type: 'db',
resource: SUBTYPE
},
cloud: {
region: getRegionFromRequest(request)
}
})

request.on('complete', function (response) {
if (response && response.error) {
const errOpts = {
skipOutcome: true
}
agent.captureError(response.error, errOpts)
span._setOutcomeFromErrorCapture(constants.OUTCOME_FAILURE)
astorm marked this conversation as resolved.
Show resolved Hide resolved
}

// we'll need to manually mark this span as async. The actual async hop
// is captured by the agent's async hooks instrumentation
span.sync = false
astorm marked this conversation as resolved.
Show resolved Hide resolved
span.end()
})

return orig.apply(request, origArguments)
}

module.exports = {
instrumentationDynamoDb,

// exported for testing
getRegionFromRequest,
getPortFromRequest,
getStatementFromRequest,
getAddressFromRequest,
getMethodFromRequest
}
Loading