sdk_contrib/hapi/lib/xray.js (125 lines of code) (raw):
const fs = require('fs');
const path = require('path');
const AWSXray = require('aws-xray-sdk-core');
const { middleware: mwUtils } = AWSXray;
const defaultOptions = {
automaticMode: true,
logger: console,
};
/**
* Sets up the plugin for use
* @param [options] Options for the plugin setup
* @param {String} [options.segmentName] Segment name to use in place of default segment name generator
* @param {Boolean} [options.captureAWS] Specifies that AWS calls should be captured. Requires aws-sdk package.
* @param {Boolean} [options.captureHTTP] Specifies that downstream http calls should be captured by default.
* @param {Boolean} [options.capturePromises] Specifies that downstream promises should be captured.
* @param {Object} [options.logger] Logger to pass to xray
* @param {Boolean} [options.automaticMode] Puts xray into automatic or manual mode. Default is true (automatic)
* @param {Object[]} [options.plugins] Array of AWS plugins to pass to xray
*/
exports.setup = (options) => {
const localOptions = { ...defaultOptions, ...options };
if (localOptions.logger) {
AWSXray.setLogger(localOptions.logger);
}
const segmentName =
localOptions.segmentName || exports._internals.createSegmentName();
mwUtils.setDefaultName(segmentName);
if (localOptions.automaticMode) {
AWSXray.enableAutomaticMode();
} else {
AWSXray.enableManualMode();
}
if (localOptions.plugins) {
AWSXray.config(localOptions.plugins);
}
if (localOptions.captureAWS) {
AWSXray.captureAWS(require('aws-sdk'));
}
if (localOptions.captureHTTP) {
AWSXray.captureHTTPsGlobal(require('http'), true);
AWSXray.captureHTTPsGlobal(require('https'), true);
}
if (localOptions.capturePromises) {
AWSXray.capturePromise();
}
};
/**
* Handles the hapi request to set up the XRay segment
*/
exports.handleRequest = async (request, h) => {
const header = mwUtils.processHeaders(request);
const name = mwUtils.resolveName(request.headers.host);
const segment = new AWSXray.Segment(
name,
header.Root || header.root,
header.Parent || header.parent
);
const { req, res } = request.raw;
res.req = req;
mwUtils.resolveSampling(header, segment, res);
segment.addIncomingRequestData(new mwUtils.IncomingRequestData(req));
mwUtils.middlewareLog('Starting Hapi XRay segment', request.url, segment);
if (AWSXray.isAutomaticMode()) {
const ns = AWSXray.getNamespace();
if (!request.plugins) {
request.plugins = {};
}
request.plugins.hapiXray = {
xrayNamespace: ns,
xrayContext: ns.createContext(),
};
ns.bindEmitter(req);
ns.bindEmitter(res);
ns.enter(request.plugins.hapiXray.xrayContext);
AWSXray.setSegment(segment);
}
request.segment = segment;
return h.continue;
};
exports.handleResponse = (request) => {
try {
exports._internals.endSegment(request);
} finally {
if (request.plugins && request.plugins.hapiXray) {
const { xrayNamespace, xrayContext } = request.plugins.hapiXray;
if (xrayNamespace && xrayContext) {
xrayNamespace.exit(xrayContext);
}
}
}
};
exports.handleError = (request, error) => {
const { segment } = request;
if (segment) {
segment.addError(error);
mwUtils.middlewareLog(
'Hapi XRay segment encountered an error',
request.url,
segment
);
}
};
exports._internals = {
endSegment: function (request) {
const { segment, response } = request;
if (!segment || segment.isClosed()) {
return;
}
if (response) {
if (response.statusCode >= 400) {
if (response.statusCode === 429) {
segment.addThrottleFlag();
}
const cause = AWSXray.utils.getCauseTypeFromHttpStatus(
response.statusCode
);
if (cause) {
segment[cause] = true;
}
}
segment.http.close(response);
}
segment.close();
mwUtils.middlewareLog(
'Closed Hapi XRay segment successfully',
request.url,
segment
);
},
/**
* Creates a name for the segment based on the package name and version
* @returns {String}
* @private
*/
createSegmentName: function () {
let segmentName = 'service';
const pkgPath = path.join(process.cwd(), 'package.json');
if (fs.existsSync(pkgPath)) {
const pjson = require(pkgPath);
segmentName = `${pjson.name || 'service'}_${pjson.version || 'v1'}`;
}
return segmentName;
},
};