diff --git a/lib/config.js b/lib/config.js index cd90a9ae6d..e19fea1256 100644 --- a/lib/config.js +++ b/lib/config.js @@ -72,6 +72,7 @@ var DEFAULTS = { stackTraceLimit: 50, transactionMaxSpans: 500, transactionSampleRate: 1.0, + useElasticTraceparentHeader: true, usePathAsTransactionName: false, verifyServerCert: true } @@ -125,6 +126,7 @@ var ENV_TABLE = { stackTraceLimit: 'ELASTIC_APM_STACK_TRACE_LIMIT', transactionMaxSpans: 'ELASTIC_APM_TRANSACTION_MAX_SPANS', transactionSampleRate: 'ELASTIC_APM_TRANSACTION_SAMPLE_RATE', + useElasticTraceparentHeader: 'ELASTIC_APM_USE_ELASTIC_TRACEPARENT_HEADER', usePathAsTransactionName: 'ELASTIC_APM_USE_PATH_AS_TRANSACTION_NAME', verifyServerCert: 'ELASTIC_APM_VERIFY_SERVER_CERT' } diff --git a/lib/instrumentation/http-shared.js b/lib/instrumentation/http-shared.js index de2c182542..e728635aa8 100644 --- a/lib/instrumentation/http-shared.js +++ b/lib/instrumentation/http-shared.js @@ -22,7 +22,7 @@ exports.instrumentRequest = function (agent, moduleName) { // don't leak previous transaction agent._instrumentation.currentTransaction = null } else { - var traceparent = req.headers['elastic-apm-traceparent'] + var traceparent = req.headers['elastic-apm-traceparent'] || req.headers.traceparent var trans = agent.startTransaction(null, null, { childOf: traceparent }) @@ -150,7 +150,11 @@ exports.traceOutgoingRequest = function (agent, moduleName, method) { // Use the transaction context as the parent, in this case. var parent = span || agent.currentTransaction if (parent && parent._context && shouldPropagateTraceContext(options)) { - options.headers['elastic-apm-traceparent'] = parent._context.toString() + const headerValue = parent._context.toString() + options.headers.traceparent = headerValue + if (agent._conf.useElasticTraceparentHeader) { + options.headers['elastic-apm-traceparent'] = headerValue + } } var req = orig.apply(this, newArgs) diff --git a/lib/lambda.js b/lib/lambda.js index 2454acd7d6..1de89892e6 100644 --- a/lib/lambda.js +++ b/lib/lambda.js @@ -76,9 +76,16 @@ module.exports = function elasticApmAwsLambda (agent) { let parentId if (payload.headers !== undefined) { for (const [key, value] of Object.entries(payload.headers)) { - if (key.toLowerCase() === 'elastic-apm-traceparent') { + const lowerCaseKey = key.toLowerCase() + if (lowerCaseKey === 'elastic-apm-traceparent') { parentId = value break + } else if (lowerCaseKey === 'traceparent') { + /** + * We do not break here because we want to make sure to use + * elastic-apm-traceparent if available. + */ + parentId = value } } } diff --git a/test/instrumentation/_agent.js b/test/instrumentation/_agent.js index 73976e33e0..f6cb30596f 100644 --- a/test/instrumentation/_agent.js +++ b/test/instrumentation/_agent.js @@ -13,12 +13,16 @@ var sharedInstrumentation module.exports = function mockAgent (expected, cb) { var agent = { - _conf: config({ - abortedErrorThreshold: '250ms', - centralConfig: false, - errorOnAbortedRequests: false, - metricsInterval: 0 - }), + _config: function (opts) { + this._conf = config( + Object.assign({ + abortedErrorThreshold: '250ms', + centralConfig: false, + errorOnAbortedRequests: false, + metricsInterval: 0 + }, opts) + ) + }, _errorFilters: new Filters(), _transactionFilters: new Filters(), _spanFilters: new Filters(), @@ -28,6 +32,7 @@ module.exports = function mockAgent (expected, cb) { }), setFramework: function () {} } + agent._config() agent._metrics = new Metrics(agent) agent._metrics.start() diff --git a/test/instrumentation/modules/http/aws.js b/test/instrumentation/modules/http/aws.js index 34a2412158..f3b2a56f2d 100644 --- a/test/instrumentation/modules/http/aws.js +++ b/test/instrumentation/modules/http/aws.js @@ -19,6 +19,7 @@ AWS.config.update({ test('non aws-sdk request', function (t) { const server = http.createServer(function (req, res) { t.equal(req.headers.authorization, undefined, 'no authorization header') + t.ok(req.headers.traceparent.length > 0, 'traceparent header') t.ok(req.headers['elastic-apm-traceparent'].length > 0, 'elastic-apm-traceparent header') res.end() server.close() @@ -40,6 +41,7 @@ test('non aws-sdk request', function (t) { test('aws-sdk request', function (t) { const server = http.createServer(function (req, res) { t.equal(req.headers.authorization.substr(0, 5), 'AWS4-', 'AWS authorization header') + t.equal(req.headers.traceparent, undefined, 'no traceparent header') t.equal(req.headers['elastic-apm-traceparent'], undefined, 'no elastic-apm-traceparent header') res.end() server.close() diff --git a/test/instrumentation/modules/http/basic.js b/test/instrumentation/modules/http/basic.js index 5fdde03905..7b7f862c56 100644 --- a/test/instrumentation/modules/http/basic.js +++ b/test/instrumentation/modules/http/basic.js @@ -82,23 +82,41 @@ test('new http.Server', function (t) { server.on('request', onRequest(t)) sendRequest(server) }) + + t.test('support elastic-apm-traceparent header', function (t) { + resetAgent(function (data) { + assert(t, data) + server.close() + t.end() + }) + + var server = new http.Server() + server.on('request', onRequest(t, true)) + sendRequest(server, undefined, true) + }) }) -function sendRequest (server, timeout) { +function sendRequest (server, timeout, useElasticHeader) { server.listen(function () { var port = server.address().port var context = TraceParent.startOrResume(null, { transactionSampleRate: 1.0 }) + const headers = {} + const contextValue = context.toString() + if (useElasticHeader) { + headers['elastic-apm-traceparent'] = contextValue + } else { + headers.traceparent = contextValue + } + var req = http.request({ hostname: 'localhost', port: port, path: '/', method: 'GET', - headers: { - 'elastic-apm-traceparent': context.toString() - } + headers: headers }, function (res) { if (timeout) throw new Error('should not get to here') res.resume() @@ -113,9 +131,9 @@ function sendRequest (server, timeout) { }) } -function onRequest (t) { +function onRequest (t, useElasticHeader) { return function onRequestHandler (req, res) { - var traceparent = req.headers['elastic-apm-traceparent'] + var traceparent = useElasticHeader ? req.headers['elastic-apm-traceparent'] : req.headers.traceparent var parent = TraceParent.fromString(traceparent) var context = agent.currentTransaction._context t.equal(parent.traceId, context.traceId, 'context trace id matches parent trace id') diff --git a/test/instrumentation/modules/http/outgoing.js b/test/instrumentation/modules/http/outgoing.js index 36382e1dc2..3e9031111b 100644 --- a/test/instrumentation/modules/http/outgoing.js +++ b/test/instrumentation/modules/http/outgoing.js @@ -30,6 +30,11 @@ test('http.request(options, callback)', echoTest('http', (port, cb) => { return http.request(options, cb) })) +test('http: consider useElasticTraceparentHeader config option', echoTest('http', { useElasticTraceparentHeader: false }, (port, cb) => { + var options = { port } + return http.request(options, cb) +})) + methods.forEach(function (name) { test(`http.${name}(urlString)`, echoTest('http', (port, cb) => { var urlString = `http://localhost:${port}` @@ -75,6 +80,11 @@ test('https.request(options, callback)', echoTest('https', (port, cb) => { return https.request(options, cb) })) +test('https: consider useElasticTraceparentHeader config option', echoTest('https', { useElasticTraceparentHeader: false }, (port, cb) => { + var options = { port, rejectUnauthorized: false } + return https.request(options, cb) +})) + methods.forEach(function (name) { test(`https.${name}(urlString, options)`, echoTest('https', (port, cb) => { var urlString = `https://localhost:${port}` @@ -111,10 +121,15 @@ methods.forEach(function (name) { } }) -function echoTest (type, handler) { +function echoTest (type, opts, handler) { + if (arguments.length === 2) { + handler = opts + opts = undefined + } + return function (t) { echoServer(type, (cp, port) => { - resetAgent(data => { + resetAgent(opts, data => { t.equal(data.transactions.length, 1, 'has one transaction') t.equal(data.spans.length, 1, 'has one span') t.equal(data.spans[0].name, 'GET localhost:' + port + '/', 'has expected span name') @@ -135,8 +150,13 @@ function echoTest (type, handler) { res.resume() }) - var traceparent = req.getHeader('elastic-apm-traceparent') - t.ok(traceparent, 'should have elastic-apm-traceparent header') + var traceparent = req.getHeader('traceparent') + t.ok(traceparent, 'should have traceparent header') + if (opts && opts.useElasticTraceparentHeader === false) { + t.equal(req.getHeader('elastic-apm-traceparent'), undefined) + } else { + t.ok(req.getHeader('elastic-apm-traceparent'), 'should have elastic-apm-traceparent header') + } var expected = TraceParent.fromString(trans._context.toString()) var received = TraceParent.fromString(traceparent) @@ -153,7 +173,8 @@ function echoTest (type, handler) { } } -function resetAgent (cb) { +function resetAgent (opts, cb) { agent._instrumentation.currentTransaction = null + agent._config(opts) agent._transport = mockClient(2, cb) } diff --git a/test/lambda/promises.js b/test/lambda/promises.js index 09850668a8..836311b161 100644 --- a/test/lambda/promises.js +++ b/test/lambda/promises.js @@ -53,7 +53,7 @@ test('resolve with parent id header present', function (t) { const input = { name: 'world', headers: { - 'Elastic-Apm-Traceparent': 'test' + traceparent: 'test' } } const output = 'Hello, world!' @@ -84,7 +84,7 @@ test('resolve with parent id header present', function (t) { t.equal(agent.transactions.length, 1) assertTransaction(t, agent.transactions[0], name, context, input, output) - t.equal(input.headers['Elastic-Apm-Traceparent'], agent.transactions[0].opts.childOf, 'context trace id matches parent trace id') + t.equal(input.headers.traceparent, agent.transactions[0].opts.childOf, 'context trace id matches parent trace id') t.end() }