Skip to content

Commit

Permalink
Support meta_struct in v0.4 (#4287)
Browse files Browse the repository at this point in the history
  • Loading branch information
uurien authored and khanayan123 committed May 16, 2024
1 parent 7637cf8 commit e7635cd
Show file tree
Hide file tree
Showing 3 changed files with 296 additions and 3 deletions.
53 changes: 50 additions & 3 deletions packages/dd-trace/src/encode/0.4.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,17 @@ class AgentEncoder {
span = formatSpan(span)
bytes.reserve(1)

if (span.type) {
if (span.type && span.meta_struct) {
bytes.buffer[bytes.length++] = 0x8d
} else if (span.type || span.meta_struct) {
bytes.buffer[bytes.length++] = 0x8c
} else {
bytes.buffer[bytes.length++] = 0x8b
}

if (span.type) {
this._encodeString(bytes, 'type')
this._encodeString(bytes, span.type)
} else {
bytes.buffer[bytes.length++] = 0x8b
}

this._encodeString(bytes, 'trace_id')
Expand All @@ -114,6 +118,10 @@ class AgentEncoder {
this._encodeMap(bytes, span.meta)
this._encodeString(bytes, 'metrics')
this._encodeMap(bytes, span.metrics)
if (span.meta_struct) {
this._encodeString(bytes, 'meta_struct')
this._encodeObject(bytes, span.meta_struct)
}
}
}

Expand Down Expand Up @@ -263,6 +271,45 @@ class AgentEncoder {
}
}

_encodeObject (bytes, value, circularReferencesDetector = new Set()) {
circularReferencesDetector.add(value)
if (Array.isArray(value)) {
return this._encodeObjectAsArray(bytes, value, circularReferencesDetector)
} else if (value !== null && typeof value === 'object') {
return this._encodeObjectAsMap(bytes, value, circularReferencesDetector)
} else if (typeof value === 'string' || typeof value === 'number') {
this._encodeValue(bytes, value)
}
}

_encodeObjectAsMap (bytes, value, circularReferencesDetector) {
const keys = Object.keys(value)
const validKeys = keys.filter(key =>
typeof value[key] === 'string' ||
typeof value[key] === 'number' ||
(value[key] !== null && typeof value[key] === 'object' && !circularReferencesDetector.has(value[key])))

this._encodeMapPrefix(bytes, validKeys.length)

for (const key of validKeys) {
this._encodeString(bytes, key)
this._encodeObject(bytes, value[key], circularReferencesDetector)
}
}

_encodeObjectAsArray (bytes, value, circularReferencesDetector) {
const validValue = value.filter(item =>
typeof item === 'string' ||
typeof item === 'number' ||
(item !== null && typeof item === 'object' && !circularReferencesDetector.has(item)))

this._encodeArrayPrefix(bytes, validValue)

for (const item of validValue) {
this._encodeObject(bytes, item, circularReferencesDetector)
}
}

_cacheString (value) {
if (!(value in this._stringMap)) {
this._stringCount++
Expand Down
219 changes: 219 additions & 0 deletions packages/dd-trace/test/encode/0.4.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,4 +235,223 @@ describe('encode', () => {
expect(trace[0].meta).to.deep.equal({ bar: 'baz', '_dd.span_links': encodedLink })
expect(trace[0].metrics).to.deep.equal({ example: 1 })
})

describe('meta_struct', () => {
it('should encode meta_struct with simple key value object', () => {
const metaStruct = {
foo: 'bar',
baz: 123
}
data[0].meta_struct = metaStruct
encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]
expect(trace[0].meta_struct).to.deep.equal(metaStruct)
})

it('should encode meta_struct with simple array of simple values', () => {
const metaStruct = ['one', 2, 'three', 4, 5, 'six']
data[0].meta_struct = metaStruct
encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]
expect(trace[0].meta_struct).to.deep.equal(metaStruct)
})

it('should encode meta_struct with array of objects', () => {
const metaStruct = [{ foo: 'bar' }, { baz: 123 }]
data[0].meta_struct = metaStruct
encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]
expect(trace[0].meta_struct).to.deep.equal(metaStruct)
})

it('should encode meta_struct with empty object and array', () => {
const metaStruct = {
foo: {},
bar: []
}
data[0].meta_struct = metaStruct
encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]
expect(trace[0].meta_struct).to.deep.equal(metaStruct)
})

it('should encode meta_struct with possible real use case', () => {
const metaStruct = {
'_dd.stack': {
exploit: [
{
type: 'test',
language: 'nodejs',
id: 'someuuid',
message: 'Threat detected',
frames: [
{
id: 0,
file: 'test.js',
line: 1,
column: 31,
function: 'test'
},
{
id: 1,
file: 'test2.js',
line: 54,
column: 77,
function: 'test'
},
{
id: 2,
file: 'test.js',
line: 1245,
column: 41,
function: 'test'
},
{
id: 3,
file: 'test3.js',
line: 2024,
column: 32,
function: 'test'
}
]
}
]
}
}
data[0].meta_struct = metaStruct

encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]
expect(trace[0].meta_struct).to.deep.equal(metaStruct)
})

it('should encode meta_struct ignoring circular references in objects', () => {
const circular = {
bar: 'baz',
deeper: {
foo: 'bar'
}
}
circular.deeper.circular = circular
const metaStruct = {
foo: circular
}
data[0].meta_struct = metaStruct

encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]

const expectedMetaStruct = {
foo: {
bar: 'baz',
deeper: {
foo: 'bar'
}
}
}
expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct)
})

it('should encode meta_struct ignoring circular references in arrays', () => {
const circular = [{
bar: 'baz'
}]
circular.push(circular)
const metaStruct = {
foo: circular
}
data[0].meta_struct = metaStruct

encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]

const expectedMetaStruct = {
foo: [{
bar: 'baz'
}]
}
expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct)
})

it('should encode meta_struct ignoring undefined properties', () => {
const metaStruct = {
foo: 'bar',
undefinedProperty: undefined
}
data[0].meta_struct = metaStruct

encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]

const expectedMetaStruct = {
foo: 'bar'
}
expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct)
})

it('should encode meta_struct ignoring null properties', () => {
const metaStruct = {
foo: 'bar',
nullProperty: null
}
data[0].meta_struct = metaStruct

encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]

const expectedMetaStruct = {
foo: 'bar'
}
expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct)
})

it('should not encode null meta_struct', () => {
data[0].meta_struct = null

encoder.encode(data)

const buffer = encoder.makePayload()

const decoded = msgpack.decode(buffer, { codec })
const trace = decoded[0]

expect(trace[0].meta_struct).to.be.undefined
})
})
})
27 changes: 27 additions & 0 deletions packages/dd-trace/test/encode/0.5.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,31 @@ describe('encode 0.5', () => {
expect(payload[5]).to.equal(1)
expect(payload[11]).to.equal(0)
})

it('should ignore meta_struct property', () => {
data[0].meta_struct = { foo: 'bar' }

encoder.encode(data)

const buffer = encoder.makePayload()
const decoded = msgpack.decode(buffer, { codec })
const stringMap = decoded[0]
const trace = decoded[1][0]

expect(trace).to.be.instanceof(Array)
expect(trace[0]).to.be.instanceof(Array)
expect(stringMap[trace[0][0]]).to.equal(data[0].service)
expect(stringMap[trace[0][1]]).to.equal(data[0].name)
expect(stringMap[trace[0][2]]).to.equal(data[0].resource)
expect(trace[0][3].toString(16)).to.equal(data[0].trace_id.toString())
expect(trace[0][4].toString(16)).to.equal(data[0].span_id.toString())
expect(trace[0][5].toString(16)).to.equal(data[0].parent_id.toString())
expect(trace[0][6].toNumber()).to.equal(data[0].start)
expect(trace[0][7].toNumber()).to.equal(data[0].duration)
expect(trace[0][8]).to.equal(0)
expect(trace[0][9]).to.deep.equal({ [stringMap.indexOf('bar')]: stringMap.indexOf('baz') })
expect(trace[0][10]).to.deep.equal({ [stringMap.indexOf('example')]: 1 })
expect(stringMap[trace[0][11]]).to.equal('') // unset
expect(trace[0][12]).to.be.undefined // Everything works the same as without meta_struct, and nothing else is added
})
})

0 comments on commit e7635cd

Please sign in to comment.