diff --git a/lib/agent.js b/lib/agent.js index b30af6867be..2c2f4afe577 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -273,13 +273,11 @@ Agent.prototype.captureError = function (err, opts, cb) { } if (req) { - var config = agent._conf.captureBody - var captureBody = config === 'errors' || config === 'all' - error.context.request = parsers.getContextFromRequest(req, captureBody) + error.context.request = parsers.getContextFromRequest(req, agent._conf, 'errors') } if (res) { - error.context.response = parsers.getContextFromResponse(res, true) + error.context.response = parsers.getContextFromResponse(res, agent._conf, true) } if (captureLocation) { diff --git a/lib/config.js b/lib/config.js index 52b785c9e4a..54053bfcff9 100644 --- a/lib/config.js +++ b/lib/config.js @@ -57,7 +57,8 @@ var DEFAULTS = { transactionMaxSpans: 500, transactionSampleRate: 1.0, serverTimeout: '30s', - disableInstrumentations: [] + disableInstrumentations: [], + captureHeaders: true } var ENV_TABLE = { @@ -92,7 +93,8 @@ var ENV_TABLE = { transactionSampleRate: 'ELASTIC_APM_TRANSACTION_SAMPLE_RATE', serverTimeout: 'ELASTIC_APM_SERVER_TIMEOUT', disableInstrumentations: 'ELASTIC_APM_DISABLE_INSTRUMENTATIONS', - payloadLogFile: 'ELASTIC_APM_PAYLOAD_LOG_FILE' + payloadLogFile: 'ELASTIC_APM_PAYLOAD_LOG_FILE', + captureHeaders: 'ELASTIC_APM_CAPTURE_HEADERS' } var BOOL_OPTS = [ @@ -103,7 +105,8 @@ var BOOL_OPTS = [ 'captureSpanStackTraces', 'errorOnAbortedRequests', 'instrument', - 'asyncHooks' + 'asyncHooks', + 'captureHeaders' ] var NUM_OPTS = [ diff --git a/lib/instrumentation/transaction.js b/lib/instrumentation/transaction.js index d36cd144a0f..0f383140826 100644 --- a/lib/instrumentation/transaction.js +++ b/lib/instrumentation/transaction.js @@ -129,13 +129,12 @@ Transaction.prototype.toJSON = function () { payload.span_count.dropped = this._droppedSpans } + var conf = this._agent._conf if (this.req) { - var config = this._agent._conf.captureBody - var captureBody = config === 'transactions' || config === 'all' - payload.context.request = parsers.getContextFromRequest(this.req, captureBody) + payload.context.request = parsers.getContextFromRequest(this.req, conf, 'transactions') } if (this.res) { - payload.context.response = parsers.getContextFromResponse(this.res) + payload.context.response = parsers.getContextFromResponse(this.res, conf) } } diff --git a/lib/parsers.js b/lib/parsers.js index e383a0173d4..7afdf767ba5 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -88,7 +88,9 @@ exports.parseError = function (err, agent, cb) { }) } -exports.getContextFromRequest = function (req, captureBody) { +exports.getContextFromRequest = function (req, conf, type) { + var captureBody = conf.captureBody === type || conf.captureBody === 'all' + var context = { http_version: req.httpVersion, method: req.method, @@ -96,8 +98,11 @@ exports.getContextFromRequest = function (req, captureBody) { socket: { remote_address: req.socket.remoteAddress, encrypted: !!req.socket.encrypted - }, - headers: Object.assign({}, req.headers) + } + } + + if (conf.captureHeaders) { + context.headers = Object.assign({}, req.headers) } var contentLength = parseInt(req.headers['content-length'], 10) @@ -123,10 +128,13 @@ exports.getContextFromRequest = function (req, captureBody) { return context } -exports.getContextFromResponse = function (res, isError) { +exports.getContextFromResponse = function (res, conf, isError) { var context = { - status_code: res.statusCode, - headers: res.headers || httpHeaders(res, true) + status_code: res.statusCode + } + + if (conf.captureHeaders) { + context.headers = res.headers || httpHeaders(res, true) } if (isError) { diff --git a/test/instrumentation/_agent.js b/test/instrumentation/_agent.js index 78a3c116ae2..8a38cfb2be0 100644 --- a/test/instrumentation/_agent.js +++ b/test/instrumentation/_agent.js @@ -26,7 +26,8 @@ module.exports = function mockAgent (expected, cb) { ignoreUserAgentStr: [], ignoreUserAgentRegExp: [], transactionSampleRate: 1.0, - disableInstrumentations: [] + disableInstrumentations: [], + captureHeaders: true }, _errorFilters: new Filters(), _transactionFilters: new Filters(), diff --git a/test/parsers.js b/test/parsers.js index 318591173a5..02e58bee23d 100644 --- a/test/parsers.js +++ b/test/parsers.js @@ -51,7 +51,7 @@ test('#getContextFromResponse()', function (t) { res.sendDate = false - var context = parsers.getContextFromResponse(res, true) + var context = parsers.getContextFromResponse(res, { captureHeaders: true }, true) t.deepEqual(context, { status_code: 200, headers: {}, @@ -72,7 +72,7 @@ test('#getContextFromResponse()', function (t) { res.sendDate = false res.write('foo') - var context = parsers.getContextFromResponse(res, true) + var context = parsers.getContextFromResponse(res, { captureHeaders: true }, true) t.deepEqual(context, { status_code: 200, headers: { connection: 'close', 'transfer-encoding': 'chunked' }, @@ -87,7 +87,7 @@ test('#getContextFromResponse()', function (t) { t.test('for error (request finished)', function (t) { onRequest(function (req, res) { req.on('end', function () { - var context = parsers.getContextFromResponse(res, true) + var context = parsers.getContextFromResponse(res, { captureHeaders: true }, true) t.deepEqual(context, { status_code: 200, headers: { connection: 'close', 'content-length': '0' }, @@ -106,7 +106,7 @@ test('#getContextFromResponse()', function (t) { t.test('for transaction', function (t) { onRequest(function (req, res) { req.on('end', function () { - var context = parsers.getContextFromResponse(res, false) + var context = parsers.getContextFromResponse(res, { captureHeaders: true }, false) t.deepEqual(context, { status_code: 200, headers: { connection: 'close', 'content-length': '0' } @@ -121,7 +121,8 @@ test('#getContextFromResponse()', function (t) { test('#getContextFromRequest()', function (t) { t.test('should parse a request object', function (t) { - var parsed = parsers.getContextFromRequest(getMockReq()) + var conf = { captureHeaders: true, captureBody: 'off' } + var parsed = parsers.getContextFromRequest(getMockReq(), conf) t.deepEqual(parsed, { http_version: '1.1', method: 'GET', @@ -146,9 +147,10 @@ test('#getContextFromRequest()', function (t) { }) t.test('full URI', function (t) { + var conf = { captureHeaders: true, captureBody: 'off' } var req = getMockReq() req.url = 'https://www.example.com:8080/some/path?key=value' - var parsed = parsers.getContextFromRequest(req) + var parsed = parsers.getContextFromRequest(req, conf) t.deepEqual(parsed.url, { pathname: '/some/path', search: '?key=value', @@ -162,9 +164,10 @@ test('#getContextFromRequest()', function (t) { }) t.test('port in host header', function (t) { + var conf = { captureHeaders: true, captureBody: 'off' } var req = getMockReq() req.headers.host = 'example.com:8080' - var parsed = parsers.getContextFromRequest(req) + var parsed = parsers.getContextFromRequest(req, conf) t.deepEqual(parsed.url, { hostname: 'example.com', port: 8080, @@ -178,9 +181,10 @@ test('#getContextFromRequest()', function (t) { }) t.test('empty query string', function (t) { + var conf = { captureHeaders: true, captureBody: 'off' } var req = getMockReq() req.url = '/some/path?' - var parsed = parsers.getContextFromRequest(req) + var parsed = parsers.getContextFromRequest(req, conf) t.deepEqual(parsed.url, { hostname: 'example.com', pathname: '/some/path', @@ -193,43 +197,47 @@ test('#getContextFromRequest()', function (t) { }) t.test('should slice too large body\'s', function (t) { + var conf = { captureHeaders: true, captureBody: 'all' } var req = getMockReq() req.body = '' for (var n = 0; n < parsers._MAX_HTTP_BODY_CHARS + 10; n++) { req.body += 'x' } req.headers['content-length'] = String(req.body.length) - var parsed = parsers.getContextFromRequest(req, true) + var parsed = parsers.getContextFromRequest(req, conf) t.equal(parsed.body.length, parsers._MAX_HTTP_BODY_CHARS) t.end() }) t.test('should not log body if opts.body is false', function (t) { + var conf = { captureHeaders: true, captureBody: 'off' } var req = getMockReq() req.body = 'secret stuff' req.headers['content-length'] = String(req.body.length) - var parsed = parsers.getContextFromRequest(req, false) + var parsed = parsers.getContextFromRequest(req, conf) t.equal(parsed.body, '[REDACTED]') t.end() }) t.test('body is object', function (t) { + var conf = { captureHeaders: true, captureBody: 'all' } var req = getMockReq() req.body = { foo: 42 } req.headers['content-length'] = JSON.stringify(req.body).length - var parsed = parsers.getContextFromRequest(req, true) + var parsed = parsers.getContextFromRequest(req, conf) t.deepEqual(parsed.body, { foo: 42 }) t.end() }) t.test('body is object, but too large', function (t) { + var conf = { captureHeaders: true, captureBody: 'all' } var req = getMockReq() req.body = { foo: '' } for (var n = 0; n < parsers._MAX_HTTP_BODY_CHARS + 10; n++) { req.body.foo += 'x' } req.headers['content-length'] = JSON.stringify(req.body).length - var parsed = parsers.getContextFromRequest(req, true) + var parsed = parsers.getContextFromRequest(req, conf) t.equal(typeof parsed.body, 'string') t.equal(parsed.body.length, parsers._MAX_HTTP_BODY_CHARS) t.equal(parsed.body.slice(0, 10), '{"foo":"xx') @@ -237,11 +245,12 @@ test('#getContextFromRequest()', function (t) { }) t.test('body is object, but not safe to stringify', function (t) { + var conf = { captureHeaders: true, captureBody: 'all' } var req = getMockReq() req.body = { foo: 42 } req.body.bar = req.body req.headers['transfer-encoding'] = 'chunked' - var parsed = parsers.getContextFromRequest(req, true) + var parsed = parsers.getContextFromRequest(req, conf) t.deepEqual(parsed.body, req.body) t.end() })