packages/core/lib/context_utils.js (135 lines of code) (raw):

/** * @module context_utils */ var cls = require('cls-hooked/context'); var logger = require('./logger'); var Segment = require('./segments/segment'); var Subsegment = require('./segments/attributes/subsegment'); var cls_mode = true; var NAMESPACE ='AWSXRay'; var SEGMENT = 'segment'; var contextOverride = false; var contextUtils = { CONTEXT_MISSING_STRATEGY: { RUNTIME_ERROR: { contextMissing: function contextMissingRuntimeError(message) { throw new Error(message); } }, LOG_ERROR: { contextMissing: function contextMissingLogError(message) { var err = new Error(message); logger.getLogger().error(err.stack); } }, IGNORE_ERROR: { contextMissing: function contextMissingIgnoreError() { } } }, contextMissingStrategy: {}, /** * Resolves the segment or subsegment given manual mode and params on the call required. * @param [Segment|Subsegment] segment - The segment manually provided via params.XraySegment, if provided. * @returns {Segment|Subsegment} * @alias module:context_utils.resolveManualSegmentParams */ resolveManualSegmentParams: function resolveManualSegmentParams(params) { if (params && !contextUtils.isAutomaticMode()) { var xraySegment = params.XRaySegment || params.XraySegment; var segment = params.Segment; var found = null; if (xraySegment && (xraySegment instanceof Segment || xraySegment instanceof Subsegment)) { found = xraySegment; delete params.XRaySegment; delete params.XraySegment; } else if (segment && (segment instanceof Segment || segment instanceof Subsegment)) { found = segment; delete params.Segment; } return found; } }, /** * Gets current CLS namespace for X-Ray SDK or creates one if absent. * @returns {Namespace} * @alias module:context_utils.getNamespace */ getNamespace: function getNamespace() { return cls.getNamespace(NAMESPACE) || cls.createNamespace(NAMESPACE); }, /** * Resolves the segment or subsegment given manual or automatic mode. * @param [Segment|Subsegment] segment - The segment manually provided, if provided. * @returns {Segment|Subsegment} * @alias module:context_utils.resolveSegment */ resolveSegment: function resolveSegment(segment) { if (cls_mode) { return this.getSegment(); } else if (segment && !cls_mode) { return segment; } else if (!segment && !cls_mode) { contextUtils.contextMissingStrategy.contextMissing('No sub/segment specified. A sub/segment must be provided for manual mode.'); } }, /** * Returns the current segment or subsegment. For use with in automatic mode only. * @returns {Segment|Subsegment} * @alias module:context_utils.getSegment */ getSegment: function getSegment() { if (cls_mode) { var segment = contextUtils.getNamespace(NAMESPACE).get(SEGMENT); if (!segment) { contextUtils.contextMissingStrategy.contextMissing('Failed to get the current sub/segment from the context.'); } else if (segment instanceof Segment && process.env.LAMBDA_TASK_ROOT && segment.facade == true) { segment.resolveLambdaTraceData(); } return segment; } else { contextUtils.contextMissingStrategy.contextMissing('Cannot get sub/segment from context. Not supported in manual mode.'); } }, /** * Sets the current segment or subsegment. For use with in automatic mode only. * @param [Segment|Subsegment] segment - The sub/segment to set. * @returns {Segment|Subsegment} * @alias module:context_utils.setSegment */ setSegment: function setSegment(segment) { if (cls_mode) { if (!contextUtils.getNamespace(NAMESPACE).set(SEGMENT, segment)) { logger.getLogger().warn('Failed to set the current sub/segment on the context.'); } } else { contextUtils.contextMissingStrategy.contextMissing('Cannot set sub/segment on context. Not supported in manual mode.'); } }, /** * Returns true if in automatic mode, otherwise false. * @returns {Segment|Subsegment} * @alias module:context_utils.isAutomaticMode */ isAutomaticMode: function isAutomaticMode() { return cls_mode; }, /** * Enables automatic mode. Automatic mode uses 'cls-hooked'. * @see https://github.com/jeff-lewis/cls-hooked * @alias module:context_utils.enableAutomaticMode */ enableAutomaticMode: function enableAutomaticMode() { cls_mode = true; contextUtils.getNamespace(NAMESPACE); logger.getLogger().debug('Overriding AWS X-Ray SDK mode. Set to automatic mode.'); }, /** * Disables automatic mode. Current segment or subsegment then must be passed manually * via the parent optional on captureFunc, captureAsyncFunc etc. * @alias module:context_utils.enableManualMode */ enableManualMode: function enableManualMode() { cls_mode = false; if (cls.getNamespace(NAMESPACE)) { cls.destroyNamespace(NAMESPACE); } logger.getLogger().debug('Overriding AWS X-Ray SDK mode. Set to manual mode.'); }, /** * Sets the context missing strategy if no context missing strategy is set using the environment variable with * key AWS_XRAY_CONTEXT_MISSING. The context missing strategy's contextMissing function will be called whenever * trace context is not found. * @param {string|function} strategy - The strategy to set. Valid string values are 'LOG_ERROR' and 'RUNTIME_ERROR'. * Alternatively, a custom function can be supplied, which takes a error message string. */ setContextMissingStrategy: function setContextMissingStrategy(strategy) { if (!contextOverride) { if (typeof strategy === 'string') { var lookupStrategy = contextUtils.CONTEXT_MISSING_STRATEGY[strategy.toUpperCase()]; if (lookupStrategy) { contextUtils.contextMissingStrategy.contextMissing = lookupStrategy.contextMissing; if (process.env.AWS_XRAY_CONTEXT_MISSING) { logger.getLogger().debug('AWS_XRAY_CONTEXT_MISSING is set. Configured context missing strategy to ' + process.env.AWS_XRAY_CONTEXT_MISSING + '.'); } else { logger.getLogger().debug('Configured context missing strategy to: ' + strategy); } } else { throw new Error('Invalid context missing strategy: ' + strategy + '. Valid values are ' + Object.keys(contextUtils.CONTEXT_MISSING_STRATEGY) + '.'); } } else if (typeof strategy === 'function') { contextUtils.contextMissingStrategy.contextMissing = strategy; logger.getLogger().info('Configured custom context missing strategy to function: ' + strategy.name); } else { throw new Error('Context missing strategy must be either a string or a custom function.'); } } else { logger.getLogger().warn('Ignoring call to setContextMissingStrategy as AWS_XRAY_CONTEXT_MISSING is set. ' + 'The current context missing strategy will not be changed.'); } } }; if (process.env.AWS_XRAY_MANUAL_MODE) { cls_mode = false; logger.getLogger().debug('Starting the AWS X-Ray SDK in manual mode.'); } else { cls.createNamespace(NAMESPACE); logger.getLogger().debug('Starting the AWS X-Ray SDK in automatic mode (default).'); } if (process.env.AWS_XRAY_CONTEXT_MISSING) { contextUtils.setContextMissingStrategy(process.env.AWS_XRAY_CONTEXT_MISSING); contextOverride = true; } else { contextUtils.contextMissingStrategy.contextMissing = contextUtils.CONTEXT_MISSING_STRATEGY.LOG_ERROR.contextMissing; logger.getLogger().debug('Using default context missing strategy: LOG_ERROR'); } module.exports = contextUtils;