Skip to content

Commit 1d6d314

Browse files
committed
[Lambda] Replace Facade segment with No-Op if trace header is missing data
1 parent 73e1fca commit 1d6d314

File tree

4 files changed

+92
-5
lines changed

4 files changed

+92
-5
lines changed

packages/core/lib/context_utils.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ var contextUtils = {
9999

100100
if (!segment) {
101101
contextUtils.contextMissingStrategy.contextMissing('Failed to get the current sub/segment from the context.');
102-
} else if (segment instanceof Segment && process.env.LAMBDA_TASK_ROOT && segment.facade == true) {
103-
segment.resolveLambdaTraceData();
102+
} else if (segment instanceof Segment && process.env.LAMBDA_TASK_ROOT) {
103+
if (segment.facade == true || segment.noOp == true) {
104+
segment.resolveLambdaTraceData();
105+
}
104106
}
105107

106108
return segment;

packages/core/lib/env/aws_lambda.js

+78-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,13 @@ module.exports.init = function init() {
3636

3737
var namespace = contextUtils.getNamespace();
3838
namespace.enter(namespace.createContext());
39-
contextUtils.setSegment(facadeSegment());
39+
40+
if (LambdaUtils.validTraceData(process.env._X_AMZN_TRACE_ID)) {
41+
contextUtils.setSegment(facadeSegment());
42+
}
43+
else {
44+
contextUtils.setSegment(noOpSegment());
45+
}
4046
};
4147

4248
var facadeSegment = function facadeSegment() {
@@ -109,3 +115,74 @@ var facadeSegment = function facadeSegment() {
109115

110116
return segment;
111117
};
118+
119+
var noOpSegment = function noOpSegment() {
120+
var segment = new Segment('no-op');
121+
var whitelistFcn = [];
122+
var silentFcn = ['addNewSubsegment', 'addSubsegment', 'removeSubsegment', 'toString', 'addSubsegmentWithoutSampling', 'addNewSubsegmentWithoutSampling', 'incrementCounter', 'decrementCounter', 'isClosed', 'close', 'format', 'flush'];
123+
var xAmznTraceId = process.env._X_AMZN_TRACE_ID;
124+
125+
for (var key in segment) {
126+
if (typeof segment[key] === 'function' && whitelistFcn.indexOf(key) === -1) {
127+
if (silentFcn.indexOf(key) === -1) {
128+
segment[key] = (function() {
129+
var func = key;
130+
return function noOp() {
131+
logger.getLogger().warn('Function "' + func + '" cannot be called on an AWS Lambda segment. Please use a subsegment to record data.');
132+
return;
133+
};
134+
})();
135+
} else {
136+
segment[key] = function noOp() {
137+
return;
138+
};
139+
}
140+
}
141+
}
142+
143+
segment.trace_id = TraceID.Invalid().toString();
144+
segment.isClosed = function() {
145+
return true;
146+
};
147+
segment.in_progress = false;
148+
segment.counter = 1;
149+
segment.notTraced = true;
150+
segment.noOp = true;
151+
152+
segment.reset = function reset() {
153+
this.trace_id = TraceID.Invalid().toString();
154+
this.id = '00000000';
155+
delete this.subsegments;
156+
this.notTraced = true;
157+
};
158+
159+
segment.resolveLambdaTraceData = function resolveLambdaTraceData() {
160+
var xAmznLambda = process.env._X_AMZN_TRACE_ID;
161+
162+
if (xAmznLambda) {
163+
164+
// This check resets the trace data whenever a new trace header is read to not leak data between invocations
165+
if (xAmznLambda != xAmznTraceIdPrev) {
166+
this.reset();
167+
168+
if (LambdaUtils.populateTraceData(segment, xAmznLambda)) {
169+
xAmznTraceIdPrev = xAmznLambda;
170+
}
171+
}
172+
} else {
173+
this.reset();
174+
contextUtils.contextMissingStrategy.contextMissing('Missing AWS Lambda trace data for X-Ray. ' +
175+
'Ensure Active Tracing is enabled and no subsegments are created outside the function handler.');
176+
}
177+
};
178+
179+
// Test for valid trace data during SDK startup. It's likely we're still in the cold-start portion of the
180+
// code at this point and a valid trace header has not been set
181+
if (LambdaUtils.validTraceData(xAmznTraceId)) {
182+
if (LambdaUtils.populateTraceData(segment, xAmznTraceId)) {
183+
xAmznTraceIdPrev = xAmznTraceId;
184+
}
185+
}
186+
187+
return segment;
188+
};

packages/core/lib/patchers/aws_p.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,11 @@ function captureAWSRequest(req) {
8888
const data = parent.segment ? parent.segment.additionalTraceData : parent.additionalTraceData;
8989

9090
var buildListener = function(req) {
91-
let traceHeader = 'Root=' + traceId + ';Parent=' + subsegment.id +
92-
';Sampled=' + (subsegment.notTraced ? '0' : '1');
91+
let traceHeader = 'Root=' + traceId;
92+
// Only append parent and sample if not in Lambda PassThrough mode
93+
if (!(parent && parent.noOp)) {
94+
traceHeader += ';Parent=' + subsegment.id + ';Sampled=' + (subsegment.notTraced ? '0' : '1');
95+
}
9396
if (data != null) {
9497
for (const [key, value] of Object.entries(data)) {
9598
traceHeader += ';' + key +'=' + value;
@@ -99,6 +102,9 @@ function captureAWSRequest(req) {
99102
};
100103

101104
var completeListener = function(res) {
105+
if (subsegment == null) {
106+
return
107+
}
102108
subsegment.addAttribute('namespace', 'aws');
103109
subsegment.addAttribute('aws', new Aws(res, subsegment.name));
104110

packages/core/lib/utils.js

+2
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ var utils = {
168168
if (!traceData) {
169169
traceData = {};
170170
logger.getLogger().error('_X_AMZN_TRACE_ID is empty or has an invalid format');
171+
} else if (segment.noOp == true && traceData.root) {
172+
valid = true;
171173
} else if (!traceData.root || !traceData.parent || !traceData.sampled) {
172174
logger.getLogger().error('_X_AMZN_TRACE_ID is missing required information');
173175
} else {

0 commit comments

Comments
 (0)