deep-framework
Version:
139 lines (112 loc) • 4.24 kB
JavaScript
/**
* Capture module.
* @module aws_p
*/
var _ = require('underscore');
var semver = require('semver');
var Aws = require('../segments/attributes/aws');
var Downstream = require('../segments/attributes/downstream');
var CLSUtils = require('../utils').CLSUtils;
var Utils = require('../utils');
var logger = require('../logger');
var minVersion = '2.7.15';
var throttledErrorOverriden = false;
var throttledError = function throttledError() {
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 (!_.isUndefined(awssdk[prop].serviceIdentifier)) {
var Service = awssdk[prop];
Service.prototype.customizeRequests(captureAWSRequest);
}
}
if (!throttledErrorOverriden) {
var newThrottledError = new awssdk.Service().throttledError;
if (typeof newThrottledError === 'function') {
throttledError = newThrottledError;
throttledErrorOverriden = true;
}
}
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);
if (!throttledErrorOverriden) {
var newThrottledError = service.throttledError;
if (typeof newThrottledError === 'function') {
throttledError = newThrottledError;
throttledErrorOverriden = true;
}
}
return service;
};
function captureAWSRequest(req) {
var parent = CLSUtils.isCLSMode() ? CLSUtils.getSegment() : (req.params ? req.params.Segment : null);
if (!parent) {
logger.info('Call ' + this.serviceIdentifier + '.' + req.operation + ' requires a segment object for tracing.');
return req;
}
if (req.params && req.params.Segment)
delete req.params.Segment;
var stack = (new Error()).stack;
var traceId = parent.segment ? parent.segment.trace_id : parent.trace_id;
var subsegment = parent.addNewSubsegment(this.serviceIdentifier);
req.on('build', function(req) {
req.httpRequest.headers['X-Amzn-Trace-Id'] = 'Root=' + traceId + '; Parent=' + subsegment.id +
'; Sampled=' + (subsegment.segment.notTraced ? '0' : '1');
}).on('complete', function(res) {
var e = res.error;
subsegment.addAttribute('namespace', 'aws');
subsegment.addAttribute('aws', new Aws(res, subsegment.name));
subsegment.addAttribute('http', new Downstream(res));
if (!_.isEmpty(e)) {
var error = { message: e.message, name: e.code, stack: stack };
if (res.httpResponse && res.httpResponse.statusCode) {
if (res.httpResponse.statusCode === 429 || throttledError(e))
subsegment.addThrottle();
subsegment.close(error, Utils.getCauseTypeFromHttpStatus(res.httpResponse.statusCode), true);
} else {
subsegment.close(error);
}
} else {
subsegment.close();
}
});
if (!req.__send) {
req.__send = req.send;
req.send = function (callback) {
if (CLSUtils.isCLSMode()) {
var session = CLSUtils.getNamespace();
session.run(function() {
CLSUtils.setSegment(subsegment);
req.__send(callback);
});
} else {
req.__send(callback);
}
};
}
}
module.exports.captureAWSClient = captureAWSClient;
module.exports.captureAWS = captureAWS;