Skip to content

Commit e7152b8

Browse files
Lambda PassThrough trace header support (#660)
1 parent 1502470 commit e7152b8

File tree

8 files changed

+110
-7
lines changed

8 files changed

+110
-7
lines changed

packages/core/lib/patchers/aws3_p.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ const getXRayMiddleware = (config: RegionResolvedConfig, manualSegment?: Segment
140140
}
141141
}
142142

143-
args.request.headers['X-Amzn-Trace-Id'] = traceHeader;
143+
if (!segment.noOp) {
144+
args.request.headers['X-Amzn-Trace-Id'] = traceHeader;
145+
}
144146

145147
let res;
146148
try {

packages/core/lib/patchers/aws_p.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ function captureAWSRequest(req) {
8888
const data = parent.segment ? parent.segment.additionalTraceData : parent.additionalTraceData;
8989

9090
var buildListener = function(req) {
91+
if (parent.noOp) {
92+
return;
93+
}
9194
let traceHeader = 'Root=' + traceId + ';Parent=' + subsegment.id +
9295
';Sampled=' + (subsegment.notTraced ? '0' : '1');
9396
if (data != null) {

packages/core/lib/patchers/http_p.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,10 @@ function enableCapture(module, downstreamXRayEnabled, subsegmentCallback) {
130130
options.headers = {};
131131
}
132132

133-
options.headers['X-Amzn-Trace-Id'] = 'Root=' + root.trace_id + ';Parent=' + subsegment.id +
134-
';Sampled=' + (subsegment.notTraced ? '0' : '1');
133+
if (!parent.noOp) {
134+
options.headers['X-Amzn-Trace-Id'] = 'Root=' + root.trace_id + ';Parent=' + subsegment.id +
135+
';Sampled=' + (subsegment.notTraced ? '0' : '1');
136+
}
135137

136138
const errorCapturer = function errorCapturer(e) {
137139
if (subsegmentCallback) {

packages/core/lib/segments/attributes/subsegment.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Subsegment.prototype.addSubsegment = function(subsegment) {
7474
subsegment.parent = this;
7575

7676
subsegment.notTraced = subsegment.parent.notTraced;
77+
subsegment.noOp = subsegment.parent.noOp;
7778

7879
if (subsegment.end_time === undefined) {
7980
this.incrementCounter(subsegment.counter);

packages/core/lib/segments/segment.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ Segment.prototype.addSubsegment = function addSubsegment(subsegment) {
248248
subsegment.parent = this;
249249

250250
subsegment.notTraced = subsegment.parent.notTraced;
251+
subsegment.noOp = subsegment.parent.noOp;
251252
this.subsegments.push(subsegment);
252253

253254
if (!subsegment.end_time) {

packages/core/lib/utils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ 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 (traceData.root && !traceData.parent && !traceData.sampled) {
172+
// Lambda PassThrough only has root, treat as valid in this case and mark the segment
173+
segment.noOp = true;
174+
valid = true;
171175
} else if (!traceData.root || !traceData.parent || !traceData.sampled) {
172176
logger.getLogger().error('_X_AMZN_TRACE_ID is missing required information');
173177
} else {

packages/core/test/unit/patchers/aws_p.test.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,4 +348,92 @@ describe('AWS patcher', function() {
348348
});
349349

350350
});
351+
352+
353+
describe('#captureAWSRequest-PassThrough-Header', function() {
354+
var awsClient, awsRequest, MyEmitter, sandbox, segment, stubResolve, addNewSubsegmentStub, sub, addNewServiceSubsegmentStub, service;
355+
356+
before(function() {
357+
MyEmitter = function() {
358+
EventEmitter.call(this);
359+
};
360+
361+
awsClient = {
362+
customizeRequests: function customizeRequests(captureAWSRequest) {
363+
this.call = captureAWSRequest;
364+
},
365+
throttledError: function throttledError() {}
366+
};
367+
awsClient = awsPatcher.captureAWSClient(awsClient);
368+
369+
util.inherits(MyEmitter, EventEmitter);
370+
});
371+
372+
beforeEach(function() {
373+
sandbox = sinon.createSandbox();
374+
375+
awsRequest = {
376+
httpRequest: {
377+
method: 'GET',
378+
url: '/',
379+
connection: {
380+
remoteAddress: 'localhost'
381+
},
382+
headers: {}
383+
},
384+
response: {}
385+
};
386+
387+
awsRequest.on = function(event, fcn) {
388+
if (event === 'complete') {
389+
this.emitter.on(event, fcn.bind(this, this.response));
390+
} else {
391+
this.emitter.on(event, fcn.bind(this, this));
392+
}
393+
return this;
394+
};
395+
396+
awsRequest.emitter = new MyEmitter();
397+
398+
segment = new Segment('testSegment', traceId);
399+
segment.noOp = true; // enforce passthrough behaviour
400+
segment.additionalTraceData = {'Foo': 'bar'};
401+
sub = segment.addNewSubsegmentWithoutSampling('subseg');
402+
service = sub.addNewSubsegmentWithoutSampling('service');
403+
404+
stubResolve = sandbox.stub(contextUtils, 'resolveSegment').returns(sub);
405+
addNewSubsegmentStub = sandbox.stub(segment, 'addNewSubsegmentWithoutSampling').returns(sub);
406+
addNewServiceSubsegmentStub = sandbox.stub(sub, 'addNewSubsegmentWithoutSampling').returns(service);
407+
});
408+
409+
afterEach(function() {
410+
sandbox.restore();
411+
});
412+
413+
it('should log an info statement and exit if parent is not found on the context or on the call params', function(done) {
414+
stubResolve.returns();
415+
var logStub = sandbox.stub(logger, 'info');
416+
417+
awsClient.call(awsRequest);
418+
419+
setTimeout(function() {
420+
logStub.should.have.been.calledOnce;
421+
done();
422+
}, 50);
423+
});
424+
425+
it('should not inject the tracing headers if passthrough mode', function(done) {
426+
sandbox.stub(contextUtils, 'isAutomaticMode').returns(true);
427+
428+
awsClient.call(awsRequest);
429+
430+
awsRequest.emitter.emit('build');
431+
432+
setTimeout(function() {
433+
assert.equal(awsRequest.httpRequest.headers['X-Amzn-Trace-Id'], undefined);
434+
done();
435+
}, 50);
436+
});
437+
438+
});
351439
});

sdk_contrib/fetch/lib/fetch_p.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ const enableCapture = function enableCapture(baseFetchFunction, requestClass, do
104104

105105
subsegment.namespace = 'remote';
106106

107-
request.headers.set('X-Amzn-Trace-Id',
108-
'Root=' + (parent.segment ? parent.segment : parent).trace_id +
109-
';Parent=' + subsegment.id +
110-
';Sampled=' + (subsegment.notTraced ? '0' : '1'));
107+
if (!parent.noOp) {
108+
request.headers.set('X-Amzn-Trace-Id',
109+
'Root=' + (parent.segment ? parent.segment : parent).trace_id +
110+
';Parent=' + subsegment.id +
111+
';Sampled=' + (subsegment.notTraced ? '0' : '1'));
112+
}
111113

112114
// Set up fetch call and capture any thrown errors
113115
const capturedFetch = async () => {

0 commit comments

Comments
 (0)