packages/core/lib/patchers/aws_p.js (126 lines of code) (raw):

/** * Capture module. * @module aws_p */ var semver = require('semver'); var Aws = require('../segments/attributes/aws'); var contextUtils = require('../context_utils'); var Utils = require('../utils'); var logger = require('../logger'); var minVersion = '2.7.15'; var throttledErrorDefault = function throttledErrorDefault() { return false; // If the customer doesn't provide an aws-sdk with a throttled error function, we can't make assumptions. }; /** * Configures the AWS SDK to automatically capture information for the segment. * All created clients will automatically be captured. See 'captureAWSClient' * for additional details. * @param {AWS} awssdk - The Javascript AWS SDK. * @alias module:aws_p.captureAWS * @returns {AWS} * @see https://github.com/aws/aws-sdk-js */ var captureAWS = function captureAWS(awssdk) { if (!semver.gte(awssdk.VERSION, minVersion)) { throw new Error ('AWS SDK version ' + minVersion + ' or greater required.'); } for (var prop in awssdk) { if (awssdk[prop].serviceIdentifier) { var Service = awssdk[prop]; Service.prototype.customizeRequests(captureAWSRequest); } } return awssdk; }; /** * Configures any AWS Client instance to automatically capture information for the segment. * For manual mode, a param with key called 'Segment' is required as a part of the AWS * call paramaters, and must reference a Segment or Subsegment object. * @param {AWS.Service} service - An instance of a AWS service to wrap. * @alias module:aws_p.captureAWSClient * @returns {AWS.Service} * @see https://github.com/aws/aws-sdk-js */ var captureAWSClient = function captureAWSClient(service) { service.customizeRequests(captureAWSRequest); return service; }; function captureAWSRequest(req) { var parent = contextUtils.resolveSegment(contextUtils.resolveManualSegmentParams(req.params)); if (!parent) { var output = this.serviceIdentifier + '.' + req.operation; if (!contextUtils.isAutomaticMode()) { logger.getLogger().info('Call ' + output + ' requires a segment object' + ' on the request params as "XRaySegment" for tracing in manual mode. Ignoring.'); } else { logger.getLogger().info('Call ' + output + ' is missing the sub/segment context for automatic mode. Ignoring.'); } return req; } var throttledError = this.throttledError || throttledErrorDefault; var stack = (new Error()).stack; let subsegment; if (parent.notTraced) { subsegment = parent.addNewSubsegmentWithoutSampling(this.serviceIdentifier); } else { subsegment = parent.addNewSubsegment(this.serviceIdentifier); } var traceId = parent.segment ? parent.segment.trace_id : parent.trace_id; const data = parent.segment ? parent.segment.additionalTraceData : parent.additionalTraceData; var buildListener = function(req) { if (parent.noOp) { return; } let traceHeader = 'Root=' + traceId + ';Parent=' + subsegment.id + ';Sampled=' + (subsegment.notTraced ? '0' : '1'); if (data != null) { for (const [key, value] of Object.entries(data)) { traceHeader += ';' + key +'=' + value; } } req.httpRequest.headers['X-Amzn-Trace-Id'] = traceHeader; }; var completeListener = function(res) { subsegment.addAttribute('namespace', 'aws'); subsegment.addAttribute('aws', new Aws(res, subsegment.name)); var httpRes = res.httpResponse; if (httpRes) { subsegment.addAttribute('http', new HttpResponse(httpRes)); if (httpRes.statusCode === 429 || (res.error && throttledError(res.error))) { subsegment.addThrottleFlag(); } } if (res.error) { var err = { message: res.error.message, name: res.error.code, stack: stack }; if (httpRes && httpRes.statusCode) { if (Utils.getCauseTypeFromHttpStatus(httpRes.statusCode) == 'error') { subsegment.addErrorFlag(); } subsegment.close(err, true); } else { subsegment.close(err); } } else { if (httpRes && httpRes.statusCode) { var cause = Utils.getCauseTypeFromHttpStatus(httpRes.statusCode); if (cause) { subsegment[cause] = true; } } subsegment.close(); } }; req.on('beforePresign', function(req) { // Only the AWS Presigner triggers this event, // so we can rely on this event to notify us when // a request is for a presigned url parent.removeSubsegment(subsegment); parent.decrementCounter(); req.removeListener('build', buildListener); req.removeListener('complete', completeListener); }); req.on('build', buildListener).on('complete', completeListener); if (!req.__send) { req.__send = req.send; req.send = function(callback) { if (contextUtils.isAutomaticMode()) { var session = contextUtils.getNamespace(); session.run(function() { contextUtils.setSegment(subsegment); req.__send(callback); }); } else { req.__send(callback); } }; } } function HttpResponse(res) { this.init(res); } HttpResponse.prototype.init = function init(res) { this.response = { status: res.statusCode || '', }; if (res.headers && res.headers['content-length']) { this.response.content_length = res.headers['content-length']; } }; module.exports.captureAWSClient = captureAWSClient; module.exports.captureAWS = captureAWS;