-
Notifications
You must be signed in to change notification settings - Fork 36
Extract trace context from ALB multiValueHeaders #647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
{ | ||
"requestContext": { | ||
"elb": { | ||
"targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/nhulston-alb-test/dcabb42f66a496e0" | ||
} | ||
}, | ||
"httpMethod": "GET", | ||
"path": "/", | ||
"multiValueQueryStringParameters": {}, | ||
"multiValueHeaders": { | ||
"accept": [ | ||
"*/*" | ||
], | ||
"accept-encoding": [ | ||
"gzip, deflate" | ||
], | ||
"accept-language": [ | ||
"*" | ||
], | ||
"connection": [ | ||
"keep-alive" | ||
], | ||
"host": [ | ||
"nhulston-test-0987654321.us-east-1.elb.amazonaws.com" | ||
], | ||
"sec-fetch-mode": [ | ||
"cors" | ||
], | ||
"traceparent": [ | ||
"00-68126c4300000000125a7f065cf9a530-1c6dcc8ab8a6e99d-01" | ||
], | ||
"tracestate": [ | ||
"dd=t.dm:-0;t.tid:68126c4300000000;s:1;p:1c6dcc8ab8a6e99d" | ||
], | ||
"user-agent": [ | ||
"node" | ||
], | ||
"x-amzn-trace-id": [ | ||
"Root=1-68126c45-01b175997ab51c4c47a2d643" | ||
], | ||
"x-datadog-parent-id": [ | ||
"1234567890" | ||
], | ||
"x-datadog-sampling-priority": [ | ||
"1" | ||
], | ||
"x-datadog-tags": [ | ||
"_dd.p.tid=68126c4300000000,_dd.p.dm=-0" | ||
], | ||
"x-datadog-trace-id": [ | ||
"0987654321" | ||
], | ||
"x-forwarded-for": [ | ||
"18.204.55.6" | ||
], | ||
"x-forwarded-port": [ | ||
"80" | ||
], | ||
"x-forwarded-proto": [ | ||
"http" | ||
] | ||
}, | ||
"body": "", | ||
"isBase64Encoded": false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { TracerWrapper } from "../../tracer-wrapper"; | ||
import { HTTPEventSubType, HTTPEventTraceExtractor } from "./http"; | ||
const albMultivalueHeadersEvent = require("../../../../event_samples/application-load-balancer-multivalue-headers.json"); | ||
|
||
let mockSpanContext: any = null; | ||
|
||
|
@@ -97,6 +98,66 @@ describe("HTTPEventTraceExtractor", () => { | |
expect(traceContext?.source).toBe("event"); | ||
}); | ||
|
||
it("extracts trace context from payload with multiValueHeaders", () => { | ||
mockSpanContext = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add an event from either ALB or API GW with real values to ensure this works? That way we can re-use it in Bottlecap too There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just commenting for visibility -- as discussed, this change only applies to ALB. API GW can have multiValueHeaders, but trace context is always injected under the regular There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But I added a real event from ALB! |
||
toTraceId: () => "123", | ||
toSpanId: () => "456", | ||
_sampling: { priority: "1" }, | ||
}; | ||
const tracerWrapper = new TracerWrapper(); | ||
const payload = { | ||
multiValueHeaders: { | ||
"X-Datadog-Trace-Id": ["123", "789"], | ||
"X-Datadog-Parent-Id": ["456"], | ||
"X-Datadog-Sampling-Priority": ["1"], | ||
}, | ||
}; | ||
const extractor = new HTTPEventTraceExtractor(tracerWrapper); | ||
const traceContext = extractor.extract(payload); | ||
|
||
expect(traceContext).not.toBeNull(); | ||
expect(spyTracerWrapper).toHaveBeenCalledWith({ | ||
"x-datadog-trace-id": "123", | ||
"x-datadog-parent-id": "456", | ||
"x-datadog-sampling-priority": "1", | ||
}); | ||
|
||
expect(traceContext?.toTraceId()).toBe("123"); | ||
expect(traceContext?.toSpanId()).toBe("456"); | ||
expect(traceContext?.sampleMode()).toBe("1"); | ||
}); | ||
|
||
it("flattens a real ALB multiValueHeaders payload into a lowercase, single-value map", () => { | ||
const tracerWrapper = new TracerWrapper(); | ||
const extractor = new HTTPEventTraceExtractor(tracerWrapper); | ||
|
||
spyTracerWrapper.mockClear(); | ||
extractor.extract(albMultivalueHeadersEvent); | ||
expect(spyTracerWrapper).toHaveBeenCalled(); | ||
|
||
const captured = spyTracerWrapper.mock.calls[0][0] as Record<string, string>; | ||
|
||
expect(captured).toEqual({ | ||
accept: "*/*", | ||
"accept-encoding": "gzip, deflate", | ||
"accept-language": "*", | ||
connection: "keep-alive", | ||
host: "nhulston-test-0987654321.us-east-1.elb.amazonaws.com", | ||
"sec-fetch-mode": "cors", | ||
"user-agent": "node", | ||
traceparent: "00-68126c4300000000125a7f065cf9a530-1c6dcc8ab8a6e99d-01", | ||
tracestate: "dd=t.dm:-0;t.tid:68126c4300000000;s:1;p:1c6dcc8ab8a6e99d", | ||
"x-amzn-trace-id": "Root=1-68126c45-01b175997ab51c4c47a2d643", | ||
"x-datadog-tags": "_dd.p.tid=68126c4300000000,_dd.p.dm=-0", | ||
"x-datadog-sampling-priority": "1", | ||
"x-datadog-trace-id": "0987654321", | ||
"x-datadog-parent-id": "1234567890", | ||
"x-forwarded-for": "18.204.55.6", | ||
"x-forwarded-port": "80", | ||
"x-forwarded-proto": "http", | ||
}); | ||
}); | ||
|
||
it("extracts trace context from payload with authorizer", () => { | ||
mockSpanContext = { | ||
toTraceId: () => "2389589954026090296", | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -41,11 +41,17 @@ export class HTTPEventTraceExtractor implements EventTraceExtractor { | |||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const headers = event.headers; | ||||||||||||||||||||||||||||||||||
const headers = event.headers ?? event.multiValueHeaders; | ||||||||||||||||||||||||||||||||||
const lowerCaseHeaders: { [key: string]: string } = {}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
for (const key of Object.keys(headers)) { | ||||||||||||||||||||||||||||||||||
lowerCaseHeaders[key.toLowerCase()] = headers[key]; | ||||||||||||||||||||||||||||||||||
for (const [key, val] of Object.entries(headers)) { | ||||||||||||||||||||||||||||||||||
if (Array.isArray(val)) { | ||||||||||||||||||||||||||||||||||
// MultiValueHeaders: take the first value | ||||||||||||||||||||||||||||||||||
lowerCaseHeaders[key.toLowerCase()] = val[0] ?? ""; | ||||||||||||||||||||||||||||||||||
} else if (typeof val === "string") { | ||||||||||||||||||||||||||||||||||
BridgeAR marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
// Single‐value header | ||||||||||||||||||||||||||||||||||
lowerCaseHeaders[key.toLowerCase()] = val; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Comment on lines
+48
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have to type check |
||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const traceContext = this.tracerWrapper.extract(lowerCaseHeaders); | ||||||||||||||||||||||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.