diff --git a/packages/dd-trace/src/encode/0.4.js b/packages/dd-trace/src/encode/0.4.js index 11cf8c4b6c7..02d96cb8a26 100644 --- a/packages/dd-trace/src/encode/0.4.js +++ b/packages/dd-trace/src/encode/0.4.js @@ -120,7 +120,7 @@ class AgentEncoder { this._encodeMap(bytes, span.metrics) if (span.meta_struct) { this._encodeString(bytes, 'meta_struct') - this._encodeObject(bytes, span.meta_struct) + this._encodeMetaStruct(bytes, span.meta_struct) } } } @@ -271,12 +271,48 @@ class AgentEncoder { } } + _encodeMetaStruct (bytes, value) { + const keys = Array.isArray(value) ? [] : Object.keys(value) + const validKeys = keys.filter(key => { + const v = value[key] + return typeof v === 'string' || + typeof v === 'number' || + (v !== null && typeof v === 'object') + }) + + this._encodeMapPrefix(bytes, validKeys.length) + + for (const key of validKeys) { + const v = value[key] + this._encodeString(bytes, key) + this._encodeObjectAsByteArray(bytes, v) + } + } + + _encodeObjectAsByteArray (bytes, value) { + const prefixLength = 5 + const offset = bytes.length + + bytes.reserve(prefixLength) + bytes.length += prefixLength + + this._encodeObject(bytes, value) + + // we should do it after encoding the object to know the real length + const length = bytes.length - offset - prefixLength + bytes.buffer[offset] = 0xc6 + bytes.buffer[offset + 1] = length >> 24 + bytes.buffer[offset + 2] = length >> 16 + bytes.buffer[offset + 3] = length >> 8 + bytes.buffer[offset + 4] = length + } + _encodeObject (bytes, value, circularReferencesDetector = new Set()) { circularReferencesDetector.add(value) if (Array.isArray(value)) { - return this._encodeObjectAsArray(bytes, value, circularReferencesDetector) + this._encodeObjectAsArray(bytes, value, circularReferencesDetector) } else if (value !== null && typeof value === 'object') { - return this._encodeObjectAsMap(bytes, value, circularReferencesDetector) + this._encodeObjectAsMap(bytes, value, circularReferencesDetector) } else if (typeof value === 'string' || typeof value === 'number') { this._encodeValue(bytes, value) } @@ -284,16 +320,19 @@ class AgentEncoder { _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]))) + const validKeys = keys.filter(key => { + const v = value[key] + return typeof v === 'string' || + typeof v === 'number' || + (v !== null && typeof v === 'object' && !circularReferencesDetector.has(v)) + }) this._encodeMapPrefix(bytes, validKeys.length) for (const key of validKeys) { + const v = value[key] this._encodeString(bytes, key) - this._encodeObject(bytes, value[key], circularReferencesDetector) + this._encodeObject(bytes, v, circularReferencesDetector) } } diff --git a/packages/dd-trace/test/encode/0.4.spec.js b/packages/dd-trace/test/encode/0.4.spec.js index cd20a318e4d..13d20250109 100644 --- a/packages/dd-trace/test/encode/0.4.spec.js +++ b/packages/dd-trace/test/encode/0.4.spec.js @@ -249,23 +249,13 @@ describe('encode', () => { 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) + expect(msgpack.decode(trace[0].meta_struct.foo)).to.be.equal(metaStruct.foo) + expect(msgpack.decode(trace[0].meta_struct.baz)).to.be.equal(metaStruct.baz) }) - it('should encode meta_struct with array of objects', () => { - const metaStruct = [{ foo: 'bar' }, { baz: 123 }] + it('should ignore array in meta_struct', () => { + const metaStruct = ['one', 2, 'three', 4, 5, 'six'] data[0].meta_struct = metaStruct encoder.encode(data) @@ -273,7 +263,7 @@ describe('encode', () => { const decoded = msgpack.decode(buffer, { codec }) const trace = decoded[0] - expect(trace[0].meta_struct).to.deep.equal(metaStruct) + expect(trace[0].meta_struct).to.deep.equal({}) }) it('should encode meta_struct with empty object and array', () => { @@ -288,7 +278,8 @@ describe('encode', () => { const decoded = msgpack.decode(buffer, { codec }) const trace = decoded[0] - expect(trace[0].meta_struct).to.deep.equal(metaStruct) + expect(msgpack.decode(trace[0].meta_struct.foo)).to.deep.equal(metaStruct.foo) + expect(msgpack.decode(trace[0].meta_struct.bar)).to.deep.equal(metaStruct.bar) }) it('should encode meta_struct with possible real use case', () => { @@ -342,7 +333,7 @@ describe('encode', () => { const decoded = msgpack.decode(buffer, { codec }) const trace = decoded[0] - expect(trace[0].meta_struct).to.deep.equal(metaStruct) + expect(msgpack.decode(trace[0].meta_struct['_dd.stack'])).to.deep.equal(metaStruct['_dd.stack']) }) it('should encode meta_struct ignoring circular references in objects', () => { @@ -373,7 +364,7 @@ describe('encode', () => { } } } - expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct) + expect(msgpack.decode(trace[0].meta_struct.foo)).to.deep.equal(expectedMetaStruct.foo) }) it('should encode meta_struct ignoring circular references in arrays', () => { @@ -398,7 +389,7 @@ describe('encode', () => { bar: 'baz' }] } - expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct) + expect(msgpack.decode(trace[0].meta_struct.foo)).to.deep.equal(expectedMetaStruct.foo) }) it('should encode meta_struct ignoring undefined properties', () => { @@ -418,7 +409,8 @@ describe('encode', () => { const expectedMetaStruct = { foo: 'bar' } - expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct) + expect(msgpack.decode(trace[0].meta_struct.foo)).to.deep.equal(expectedMetaStruct.foo) + expect(trace[0].meta_struct.undefinedProperty).to.be.undefined }) it('should encode meta_struct ignoring null properties', () => { @@ -438,7 +430,8 @@ describe('encode', () => { const expectedMetaStruct = { foo: 'bar' } - expect(trace[0].meta_struct).to.deep.equal(expectedMetaStruct) + expect(msgpack.decode(trace[0].meta_struct.foo)).to.deep.equal(expectedMetaStruct.foo) + expect(trace[0].meta_struct.nullProperty).to.be.undefined }) it('should not encode null meta_struct', () => {