Repository: honeycombio/beeline-nodejs
Pull request #206 (2020-06-04 16:30:05)

Add common request headers to auto-instrumented fields.
The ruby beeline instruments Accept, Content-Type and User Agent. In addition, we want X-Forwarded-For and similar headers for cases when a service is deployed behind a proxy or load balancer and remote_addr might be obscured.


Patch:
diff --git a/lib/instrumentation/express.js b/lib/instrumentation/express.js
index 09c1b23..6628795 100644
--- a/lib/instrumentation/express.js
+++ b/lib/instrumentation/express.js
@@ -88,24 +88,33 @@ const getMagicMiddleware = ({ userContext, traceIdSource, parentIdSource, packag
   if (parentTraceId) {
     traceContext.parentSpanId = parentTraceId;
   }
+  let fields = {
+    [schema.EVENT_TYPE]: "express",
+    [schema.PACKAGE_VERSION]: packageVersion,
+    [schema.TRACE_SPAN_NAME]: "request",
+    [schema.TRACE_ID_SOURCE]: traceContext.source,
+    "request.host": req.hostname,
+    "request.original_url": req.originalUrl,
+    "request.remote_addr": req.ip,
+    "request.secure": req.secure,
+    "request.method": req.method,
+    "request.scheme": req.protocol,
+    "request.path": req.path,
+    "request.query": req.query,
+    "request.http_version": `HTTP/${req.httpVersion}`,
+    "request.fresh": req.fresh,
+    "request.xhr": req.xhr,
+  };
+
+  for (let [key, value] of Object.entries(traceUtil.getInstrumentedRequestHeaders())) {
+    let header = req.get(key);
+    if (undefined != header) {
+      fields[value] = header;
+    }
+  }
+
   let rootSpan = api.startTrace(
-    {
-      [schema.EVENT_TYPE]: "express",
-      [schema.PACKAGE_VERSION]: packageVersion,
-      [schema.TRACE_SPAN_NAME]: "request",
-      [schema.TRACE_ID_SOURCE]: traceContext.source,
-      "request.host": req.hostname,
-      "request.original_url": req.originalUrl,
-      "request.remote_addr": req.ip,
-      "request.secure": req.secure,
-      "request.method": req.method,
-      "request.scheme": req.protocol,
-      "request.path": req.path,
-      "request.query": req.query,
-      "request.http_version": `HTTP/${req.httpVersion}`,
-      "request.fresh": req.fresh,
-      "request.xhr": req.xhr,
-    },
+    fields,
     traceContext.traceId,
     traceContext.parentSpanId,
     traceContext.dataset
diff --git a/lib/instrumentation/fastify.js b/lib/instrumentation/fastify.js
index ce2e5c8..ba7674b 100644
--- a/lib/instrumentation/fastify.js
+++ b/lib/instrumentation/fastify.js
@@ -75,20 +75,28 @@ const instrumentFastify = function(fastify, opts = {}) {
       if (parentTraceId) {
         traceContext.parentSpanId = parentTraceId;
       }
+      let fields = {
+        [schema.EVENT_TYPE]: "fastify",
+        [schema.PACKAGE_VERSION]: opts.packageVersion,
+        [schema.TRACE_SPAN_NAME]: "request",
+        [schema.TRACE_ID_SOURCE]: traceContext.source,
+        "request.host": request.hostname,
+        "request.original_url": request.req.originalUrl,
+        "request.remote_addr": request.ip,
+        "request.method": request.req.method,
+        "request.route": request.route ? request.route.path : undefined,
+        "request.query": request.query,
+        "request.http_version": `HTTP/${request.req.httpVersion}`,
+      };
+
+      for (let [key, value] of Object.entries(traceUtil.getInstrumentedRequestHeaders())) {
+        let header = request.headers[key.toLowerCase()];
+        if (undefined != header) {
+          fields[value] = header;
+        }
+      }
       let rootSpan = api.startTrace(
-        {
-          [schema.EVENT_TYPE]: "fastify",
-          [schema.PACKAGE_VERSION]: opts.packageVersion,
-          [schema.TRACE_SPAN_NAME]: "request",
-          [schema.TRACE_ID_SOURCE]: traceContext.source,
-          "request.host": request.hostname,
-          "request.original_url": request.req.originalUrl,
-          "request.remote_addr": request.ip,
-          "request.method": request.req.method,
-          "request.route": request.route ? request.route.path : undefined,
-          "request.query": request.query,
-          "request.http_version": `HTTP/${request.req.httpVersion}`,
-        },
+        fields,
         traceContext.traceId,
         traceContext.parentSpanId,
         traceContext.dataset
diff --git a/lib/instrumentation/trace-util.js b/lib/instrumentation/trace-util.js
index ecafa81..597fe0f 100644
--- a/lib/instrumentation/trace-util.js
+++ b/lib/instrumentation/trace-util.js
@@ -128,3 +128,14 @@ exports.getUserContext = (userContext, req) => {
   }
   return userEventContext;
 };
+
+exports.getInstrumentedRequestHeaders = () => {
+  return {
+    "X-Forwarded-For": "request.header.x_forwarded_for",
+    "X-Forwarded-Proto": "request.header.x_forwarded_proto",
+    "X-Forwarded-Port": "request.header.x_forwarded_port",
+    "User-Agent": "request.header.user_agent",
+    "Content-Type": "request.header.content_type",
+    Accept: "request.header.accept",
+  };
+};


Test patch:
diff --git a/lib/instrumentation/express.test.js b/lib/instrumentation/express.test.js
index 5d5baf6..0b2f3e8 100644
--- a/lib/instrumentation/express.test.js
+++ b/lib/instrumentation/express.test.js
@@ -56,6 +56,7 @@ describe("userContext", () => {
             "request.method": "GET",
             "request.scheme": "http",
             "request.path": "/",
+            "request.header.user_agent": expect.stringContaining("superagent"),
             "request.query": {},
             "request.http_version": "HTTP/1.1",
             "request.fresh": false,
diff --git a/lib/instrumentation/fastify.test.js b/lib/instrumentation/fastify.test.js
index fe8c159..c2a1ec8 100644
--- a/lib/instrumentation/fastify.test.js
+++ b/lib/instrumentation/fastify.test.js
@@ -61,6 +61,7 @@ describe("userContext", () => {
                 "request.route": undefined,
                 "request.original_url": "/",
                 "request.remote_addr": "127.0.0.1",
+                "request.header.user_agent": expect.stringContaining("superagent"),
                 "request.method": "GET",
                 "request.query": {},
                 "request.http_version": "HTTP/1.1",