-
Notifications
You must be signed in to change notification settings - Fork 36
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
breaking: Use sha256 to hash lambda traceId that are triggered by Step Functions and set _dd.p.tid #534
breaking: Use sha256 to hash lambda traceId that are triggered by Step Functions and set _dd.p.tid #534
Changes from 26 commits
131f778
b9032cd
aa1a0fa
60e0445
1617e1a
858cfd5
3338526
3c7b456
89a3ce3
43fe7b8
4ed002f
1a22e0a
ee7d6bd
eb49ebf
f3c87db
e8470cd
d9c4072
b3d76cd
36f3942
a3b7bd7
e3be794
848a3b2
0de000f
0c3dbbd
cf96c98
8e28404
0debb2a
1837ffe
b385614
d3e6fbf
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 |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { StepFunctionContextService } from "./step-function-service"; | ||
import { PARENT_ID, StepFunctionContextService } from "./step-function-service"; | ||
|
||
describe("StepFunctionContextService", () => { | ||
const stepFunctionEvent = { | ||
|
@@ -194,8 +194,8 @@ describe("StepFunctionContextService", () => { | |
|
||
expect(spanContext).not.toBeNull(); | ||
|
||
expect(spanContext?.toTraceId()).toBe("947965466153612645"); | ||
expect(spanContext?.toSpanId()).toBe("4602916161841036335"); | ||
expect(spanContext?.toTraceId()).toBe("1139193989631387307"); | ||
expect(spanContext?.toSpanId()).toBe("5892738536804826142"); | ||
expect(spanContext?.sampleMode()).toBe("1"); | ||
expect(spanContext?.source).toBe("event"); | ||
}); | ||
|
@@ -211,54 +211,55 @@ describe("StepFunctionContextService", () => { | |
}); | ||
}); | ||
|
||
describe("deterministicMd5HashToBigIntString", () => { | ||
describe("deterministicSha256HashToBigIntString", () => { | ||
it("returns the same hash number generated in `logs backend` for a random string", () => { | ||
const instance = StepFunctionContextService.instance(); | ||
const hash = instance["deterministicMd5HashToBigIntString"]("some_testing_random_string"); | ||
expect(hash).toEqual("2251275791555400689"); | ||
const hash = instance["deterministicSha256HashToBigIntString"]("some_testing_random_string", PARENT_ID); | ||
expect(hash).toEqual("4364271812988819936"); | ||
}); | ||
|
||
it("returns the same hash number generated in `logs backend` for execution id # state name # entered time", () => { | ||
const instance = StepFunctionContextService.instance(); | ||
const hash = instance["deterministicMd5HashToBigIntString"]( | ||
const hash = instance["deterministicSha256HashToBigIntString"]( | ||
"arn:aws:states:sa-east-1:601427271234:express:DatadogStateMachine:acaf1a67-336a-e854-1599-2a627eb2dd8a:c8baf081-31f1-464d-971f-70cb17d01111#step-one#2022-12-08T21:08:19.224Z", | ||
PARENT_ID, | ||
); | ||
expect(hash).toEqual("8034507082463708833"); | ||
expect(hash).toEqual("4340734536022949921"); | ||
}); | ||
}); | ||
|
||
describe("deterministicMd5HashInBinary", () => { | ||
describe("deterministicSha256Hash", () => { | ||
it.each([ | ||
[ | ||
"a random string", | ||
"some_testing_random_string", | ||
"0001111100111110001000110110011110010111000110001001001111110001", | ||
"0011110010010001000000100001011101001100011100101101100111100000", | ||
], | ||
[ | ||
"an execution id", | ||
"arn:aws:states:sa-east-1:601427271234:express:DatadogStateMachine:acaf1a67-336a-e854-1599-2a627eb2dd8a:c8baf081-31f1-464d-971f-70cb17d041f4", | ||
"0010010000101100100000101011111101111100110110001110111100111101", | ||
"0100010100110010010010100001011001110100111011010100110010000100", | ||
], | ||
[ | ||
"another execution id", | ||
"arn:aws:states:sa-east-1:601427271234:express:DatadogStateMachine:acaf1a67-336a-e854-1599-2a627eb2dd8a:c8baf081-31f1-464d-971f-70cb17d01111", | ||
"0010001100110000011011011111010000100111100000110000100100101010", | ||
"0010111110001100100010000101001100110000000000010111011100101011", | ||
], | ||
[ | ||
"execution id # state name # entered time", | ||
"arn:aws:states:sa-east-1:601427271234:express:DatadogStateMachine:acaf1a67-336a-e854-1599-2a627eb2dd8a:c8baf081-31f1-464d-971f-70cb17d01111#step-one#2022-12-08T21:08:19.224Z", | ||
"0110111110000000010011011001111101110011100111000000011010100001", | ||
"0011110000111101011000110000111111110011111010110000000000100001", | ||
], | ||
])("returns the same hash number generated in `logs backend` for %s", (_, str, expected) => { | ||
const instance = StepFunctionContextService.instance(); | ||
const hash = instance["deterministicMd5HashInBinary"](str); | ||
const hash = instance["deterministicSha256Hash"](str, PARENT_ID); | ||
expect(hash).toEqual(expected); | ||
}); | ||
|
||
it("returns a hash always leading with 0", () => { | ||
const instance = StepFunctionContextService.instance(); | ||
for (let i = 0; i < 20; i++) { | ||
const hash = instance["deterministicMd5HashInBinary"](i.toString()); | ||
const hash = instance["deterministicSha256Hash"](i.toString(), PARENT_ID); | ||
expect(hash.substring(0, 1)).toMatch("0"); | ||
} | ||
}); | ||
|
@@ -268,36 +269,44 @@ describe("StepFunctionContextService", () => { | |
const times = 20; | ||
for (let i = 0; i < times; i++) { | ||
for (let j = i + 1; j < times; j++) { | ||
const hash1 = instance["deterministicMd5HashInBinary"](i.toString()); | ||
const hash2 = instance["deterministicMd5HashInBinary"](j.toString()); | ||
const hash1 = instance["deterministicSha256Hash"](i.toString(), PARENT_ID); | ||
const hash2 = instance["deterministicSha256Hash"](j.toString(), PARENT_ID); | ||
expect(hash1).not.toMatch(hash2); | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
describe("hexToBinary", () => { | ||
describe("numberToBinaryString", () => { | ||
const instance = StepFunctionContextService.instance(); | ||
it.each([ | ||
["0", "0000"], | ||
["1", "0001"], | ||
["2", "0010"], | ||
["3", "0011"], | ||
["4", "0100"], | ||
["5", "0101"], | ||
["6", "0110"], | ||
["7", "0111"], | ||
["8", "1000"], | ||
["9", "1001"], | ||
["a", "1010"], | ||
["b", "1011"], | ||
["c", "1100"], | ||
["d", "1101"], | ||
["e", "1110"], | ||
["f", "1111"], | ||
[0, "00000000"], | ||
[1, "00000001"], | ||
[2, "00000010"], | ||
[3, "00000011"], | ||
[4, "00000100"], | ||
])("returns the right binary number for %s => %s", (hex, expected) => { | ||
const binary = instance["hexToBinary"](hex); | ||
const binary = instance["numberToBinaryString"](hex); | ||
expect(binary).toBe(expected); | ||
}); | ||
}); | ||
|
||
describe("test 64 bits deterministicSha256HashToBigIntString for span id", () => { | ||
const instance = StepFunctionContextService.instance(); | ||
it("first test of #1", () => { | ||
const actual = instance["deterministicSha256HashToBigIntString"]( | ||
"arn:aws:states:sa-east-1:425362996713:stateMachine:MyStateMachine-b276uka1j#lambda#1", | ||
PARENT_ID, | ||
); | ||
expect(actual).toEqual("3711631873188331089"); | ||
}); | ||
|
||
it("test same hashing number is generated as logs-backend for execution id # state name # entered time", () => { | ||
const actual = instance["deterministicSha256HashToBigIntString"]( | ||
"arn:aws:states:sa-east-1:425362996713:stateMachine:MyStateMachine-b276uka1j#lambda#2", | ||
PARENT_ID, | ||
); | ||
expect(actual).toEqual("5759173372325510050"); | ||
}); | ||
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. this matches logs-backend's hash result |
||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import { Md5 } from "ts-md5"; | ||
import { logDebug } from "../utils"; | ||
import { SampleMode, TraceSource } from "./trace-context-service"; | ||
import { SpanContextWrapper } from "./span-context-wrapper"; | ||
import { Sha256 } from "@aws-crypto/sha256-js"; | ||
|
||
export interface StepFunctionContext { | ||
"step_function.execution_name": string; | ||
|
@@ -16,6 +16,10 @@ export interface StepFunctionContext { | |
"step_function.state_retry_count": number; | ||
} | ||
|
||
export const TRACE_ID = "traceId"; | ||
export const PARENT_ID = "spanId"; | ||
export const DD_P_TID = "_dd.p.tid"; | ||
|
||
export class StepFunctionContextService { | ||
private static _instance: StepFunctionContextService; | ||
public context?: StepFunctionContext; | ||
|
@@ -123,51 +127,65 @@ export class StepFunctionContextService { | |
public get spanContext(): SpanContextWrapper | null { | ||
if (this.context === undefined) return null; | ||
|
||
const traceId = this.deterministicMd5HashToBigIntString(this.context["step_function.execution_id"]); | ||
const parentId = this.deterministicMd5HashToBigIntString( | ||
const traceId = this.deterministicSha256HashToBigIntString(this.context["step_function.execution_id"], TRACE_ID); | ||
const parentId = this.deterministicSha256HashToBigIntString( | ||
this.context["step_function.execution_id"] + | ||
"#" + | ||
this.context["step_function.state_name"] + | ||
"#" + | ||
this.context["step_function.state_entered_time"], | ||
PARENT_ID, | ||
); | ||
const sampleMode = SampleMode.AUTO_KEEP; | ||
const _DatadogSpanContext = require("dd-trace/packages/dd-trace/src/opentracing/span_context"); | ||
const id = require("dd-trace/packages/dd-trace/src/id"); | ||
|
||
const spanContext = SpanContextWrapper.fromTraceContext({ | ||
traceId, | ||
parentId, | ||
sampleMode, | ||
source: TraceSource.Event, | ||
const ddTraceContext = new _DatadogSpanContext({ | ||
traceId: id(traceId, 10), | ||
spanId: id(parentId, 10), | ||
sampling: { priority: sampleMode.toString(2) }, | ||
}); | ||
|
||
const ptid = this.deterministicSha256HashToBigIntString(this.context["step_function.execution_id"], DD_P_TID); | ||
ddTraceContext._trace.tags["_dd.p.tid"] = id(ptid, 10).toString(16); | ||
const spanContext = new SpanContextWrapper(ddTraceContext, TraceSource.Event); | ||
|
||
if (spanContext === null) return null; | ||
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. Why are we not using the current If so, is there another way to not require 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.
|
||
logDebug(`Extracted trace context from StepFunctionContext`, { traceContext: this.context }); | ||
return spanContext; | ||
} | ||
|
||
private deterministicMd5HashToBigIntString(s: string): string { | ||
const binaryString = this.deterministicMd5HashInBinary(s); | ||
private deterministicSha256HashToBigIntString(s: string, type: string): string { | ||
const binaryString = this.deterministicSha256Hash(s, type); | ||
return BigInt("0b" + binaryString).toString(); | ||
} | ||
|
||
private deterministicMd5HashInBinary(s: string): string { | ||
// Md5 here is used here because we don't need a cryptographically secure hashing method but to generate the same trace/span ids as the backend does | ||
const hex = Md5.hashStr(s); | ||
|
||
let binary = ""; | ||
for (let i = 0; i < hex.length; i++) { | ||
const ch = hex.charAt(i); | ||
binary = binary + this.hexToBinary(ch); | ||
private deterministicSha256Hash(s: string, type: string): string { | ||
// returns 128 bits hash unless mostSignificant64Bits options is set to true. | ||
|
||
const hash = new Sha256(); | ||
hash.update(s); | ||
const uint8Array = hash.digestSync(); | ||
let intArray; | ||
if (type === TRACE_ID) { | ||
intArray = uint8Array.subarray(8, 16); | ||
} else { | ||
// type === SPAN_ID || type === DD_P_TID | ||
intArray = uint8Array.subarray(0, 8); | ||
} | ||
let binaryString = ""; | ||
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. This section matches the logic that we put on logs-backend implementation. |
||
for (const num of intArray) { | ||
kimi-p marked this conversation as resolved.
Show resolved
Hide resolved
|
||
binaryString = binaryString + this.numberToBinaryString(num); | ||
} | ||
|
||
const res = "0" + binary.substring(1, 64); | ||
kimi-p marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const res = "0" + binaryString.substring(1, 64); | ||
if (res === "0".repeat(64)) { | ||
return "1"; | ||
} | ||
return res; | ||
} | ||
|
||
private hexToBinary(hex: string) { | ||
return parseInt(hex, 16).toString(2).padStart(4, "0"); | ||
private numberToBinaryString(num: number): string { | ||
return num.toString(2).padStart(8, "0"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this matches logs-backend PR's hashing result
https://github.com/DataDog/logs-backend/pull/71616/files#diff-820e0a1dd4f0f97815ac54f993c08b3c44c67dd26a312968c214f16ab73494c0R15