Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1c6c7ac

Browse files
authored
fix: send telemetry metadata before process spawn (#6375)
* fix: send telemetry metadata before process spawn * fix: update integration test helpers for correct telemetry metadata validation * fix: allow unknown values in telemetry metadata validation * fix: restore atomic telemetry for abort and abort.runtime points * fix: remove trailing spaces and fix line length in helpers * fix: remove remaining trailing space
1 parent 5e60713 commit 1c6c7ac

File tree

5 files changed

+88
-68
lines changed

5 files changed

+88
-68
lines changed

integration-tests/helpers/index.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ function assertTelemetryPoints (pid, msgs, expectedTelemetryPoints) {
7878
let points = []
7979
for (const [telemetryType, data] of msgs) {
8080
assert.strictEqual(telemetryType, 'library_entrypoint')
81-
assert.deepStrictEqual(data.metadata, meta(pid))
81+
assertMetadata(data.metadata, pid)
8282
points = points.concat(data.points)
8383
}
8484
const expectedPoints = getPoints(...expectedTelemetryPoints)
@@ -106,18 +106,34 @@ function assertTelemetryPoints (pid, msgs, expectedTelemetryPoints) {
106106
return expectedPoints
107107
}
108108

109-
function meta (pid) {
110-
return {
109+
function assertMetadata (actualMetadata, pid) {
110+
const expectedBasicMetadata = {
111111
language_name: 'nodejs',
112112
language_version: process.versions.node,
113113
runtime_name: 'nodejs',
114114
runtime_version: process.versions.node,
115115
tracer_version: require('../../package.json').version,
116-
pid: Number(pid),
117-
result: 'unknown',
118-
result_reason: 'unknown',
119-
result_class: 'unknown'
116+
pid: Number(pid)
120117
}
118+
119+
// Validate basic metadata
120+
for (const key of Object.keys(expectedBasicMetadata)) {
121+
assert.strictEqual(actualMetadata[key], expectedBasicMetadata[key])
122+
}
123+
124+
// Validate result metadata is present and has valid values
125+
assert(actualMetadata.result, 'result field should be present')
126+
assert(actualMetadata.result_class, 'result_class field should be present')
127+
assert(actualMetadata.result_reason, 'result_reason field should be present')
128+
129+
// Check that result metadata has expected values for telemetry scenarios
130+
const validResults = ['success', 'abort', 'error', 'unknown']
131+
const validResultClasses = ['success', 'incompatible_runtime', 'incompatible_library', 'internal_error', 'unknown']
132+
133+
assert(validResults.includes(actualMetadata.result), `Invalid result: ${actualMetadata.result}`)
134+
assert(validResultClasses.includes(actualMetadata.result_class),
135+
`Invalid result_class: ${actualMetadata.result_class}`)
136+
assert(typeof actualMetadata.result_reason === 'string', 'result_reason should be a string')
121137
}
122138
}
123139

packages/datadog-instrumentations/src/helpers/register.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,11 @@ for (const packageName of names) {
146146
`error_type:${e.constructor.name}`,
147147
`integration:${name}`,
148148
`integration_version:${version}`
149-
])
149+
], {
150+
result: 'error',
151+
result_class: 'internal_error',
152+
result_reason: `Error during instrumentation of ${name}@${version}: ${e.message}`
153+
})
150154
}
151155
namesAndSuccesses[`${name}@${version}`] = true
152156
}
@@ -160,7 +164,11 @@ for (const packageName of names) {
160164
telemetry('abort.integration', [
161165
`integration:${name}`,
162166
`integration_version:${version}`
163-
])
167+
], {
168+
result: 'abort',
169+
result_class: 'incompatible_library',
170+
result_reason: `Incompatible integration version: ${name}@${version}`
171+
})
164172
log.info('Found incompatible integration version: %s', nameVersion)
165173
seenCombo.add(nameVersion)
166174
}

packages/dd-trace/src/guardrails/index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ function guard (fn) {
4545
telemetry([
4646
{ name: 'abort', tags: ['reason:incompatible_runtime'] },
4747
{ name: 'abort.runtime', tags: [] }
48-
])
48+
], undefined, {
49+
result: 'abort',
50+
result_class: 'incompatible_runtime',
51+
result_reason: 'Incompatible runtime nodejs ' + version + ', supported runtimes: nodejs ' + engines.node
52+
})
4953
log.info('Aborting application instrumentation due to incompatible_runtime.')
5054
log.info('Found incompatible runtime nodejs %s, Supported runtimes: nodejs %s.', version, engines.node)
5155
if (forced) {
@@ -56,7 +60,11 @@ function guard (fn) {
5660
if (!clobberBailout && (!initBailout || forced)) {
5761
// Ensure the instrumentation source is set for the current process and potential child processes.
5862
var result = fn()
59-
telemetry('complete', ['injection_forced:' + (forced && initBailout ? 'true' : 'false')])
63+
telemetry('complete', ['injection_forced:' + (forced && initBailout ? 'true' : 'false')], {
64+
result: 'success',
65+
result_class: 'success',
66+
result_reason: 'Successfully configured ddtrace package'
67+
})
6068
log.info('Application instrumentation bootstrapping complete')
6169
return result
6270
}

packages/dd-trace/src/guardrails/telemetry.js

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function shouldSend (point) {
4747
return true
4848
}
4949

50-
function sendTelemetry (name, tags) {
50+
function sendTelemetry (name, tags, resultMetadata) {
5151
var points = name
5252
if (typeof name === 'string') {
5353
points = [{ name: name, tags: tags || [] }]
@@ -62,32 +62,31 @@ function sendTelemetry (name, tags) {
6262
if (points.length === 0) {
6363
return
6464
}
65+
66+
// Update metadata with provided result metadata
67+
var currentMetadata = {}
68+
for (var key in metadata) {
69+
currentMetadata[key] = metadata[key]
70+
}
71+
if (resultMetadata) {
72+
for (var resultKey in resultMetadata) {
73+
currentMetadata[resultKey] = resultMetadata[resultKey]
74+
}
75+
}
76+
6577
var proc = spawn(process.env.DD_TELEMETRY_FORWARDER_PATH, ['library_entrypoint'], {
6678
stdio: 'pipe'
6779
})
6880
proc.on('error', function () {
6981
log.error('Failed to spawn telemetry forwarder')
70-
metadata.result = 'error'
71-
metadata.result_class = 'internal_error'
72-
metadata.result_reason = 'Failed to spawn telemetry forwarder'
7382
})
7483
proc.on('exit', function (code) {
75-
if (code === 0) {
76-
metadata.result = 'success'
77-
metadata.result_class = 'success'
78-
metadata.result_reason = 'Successfully configured ddtrace package'
79-
} else {
84+
if (code !== 0) {
8085
log.error('Telemetry forwarder exited with code', code)
81-
metadata.result = 'error'
82-
metadata.result_class = 'internal_error'
83-
metadata.result_reason = 'Telemetry forwarder exited with code ' + code
8486
}
8587
})
8688
proc.stdin.on('error', function () {
8789
log.error('Failed to write telemetry data to telemetry forwarder')
88-
metadata.result = 'error'
89-
metadata.result_class = 'internal_error'
90-
metadata.result_reason = 'Failed to write telemetry data to telemetry forwarder'
9190
})
92-
proc.stdin.end(JSON.stringify({ metadata: metadata, points: points }))
91+
proc.stdin.end(JSON.stringify({ metadata: currentMetadata, points: points }))
9392
}

packages/dd-trace/test/guardrails/telemetry.spec.js

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('sendTelemetry', () => {
7373
})
7474
})
7575

76-
describe('Error scenarios and metadata', () => {
76+
describe('Result metadata parameter', () => {
7777
let mockProc, telemetryModule, capturedStdinData
7878

7979
function createMockProcess () {
@@ -91,28 +91,6 @@ describe('sendTelemetry', () => {
9191
})
9292
}
9393

94-
function runTelemetry (eventType, value) {
95-
const originalStringify = JSON.stringify
96-
JSON.stringify = function (obj) {
97-
if (obj && obj.metadata && obj.points) {
98-
if (eventType === 'spawn-error') {
99-
mockProc.emit('error', new Error(value))
100-
} else if (eventType === 'exit') {
101-
mockProc.emit('exit', value)
102-
} else if (eventType === 'stdin-error') {
103-
mockProc.stdin.emit('error', new Error(value))
104-
}
105-
}
106-
return originalStringify.apply(this, arguments)
107-
}
108-
109-
try {
110-
telemetryModule([{ name: 'test', tags: [] }])
111-
} finally {
112-
JSON.stringify = originalStringify
113-
}
114-
}
115-
11694
function assertStdinMetadata (expected) {
11795
expect(capturedStdinData).to.exist
11896
const parsed = JSON.parse(capturedStdinData)
@@ -127,43 +105,54 @@ describe('sendTelemetry', () => {
127105
telemetryModule = loadTelemetryModuleWithMockProc()
128106
})
129107

130-
it('should set error metadata when telemetry forwarder fails to spawn', () => {
131-
runTelemetry('spawn-error', 'Spawn failed')
108+
it('should use provided result metadata', () => {
109+
telemetryModule([{ name: 'error', tags: ['integration:express'] }], undefined, {
110+
result: 'error',
111+
result_class: 'internal_error',
112+
result_reason: 'Error during instrumentation of [email protected]: TypeError'
113+
})
132114

133115
assertStdinMetadata({
134116
result: 'error',
135117
result_class: 'internal_error',
136-
result_reason: 'Failed to spawn telemetry forwarder'
118+
result_reason: 'Error during instrumentation of [email protected]: TypeError'
137119
})
138120
})
139121

140-
it('should set error metadata when telemetry forwarder exits with non-zero code', () => {
141-
runTelemetry('exit', 1)
122+
it('should use provided result metadata for abort scenarios', () => {
123+
telemetryModule('abort.integration', ['integration:redis'], {
124+
result: 'abort',
125+
result_class: 'incompatible_library',
126+
result_reason: 'Incompatible integration version: [email protected]'
127+
})
142128

143129
assertStdinMetadata({
144-
result: 'error',
145-
result_class: 'internal_error',
146-
result_reason: 'Telemetry forwarder exited with code 1'
130+
result: 'abort',
131+
result_class: 'incompatible_library',
132+
result_reason: 'Incompatible integration version: [email protected]'
147133
})
148134
})
149135

150-
it('should set error metadata when writing to telemetry forwarder fails', () => {
151-
runTelemetry('stdin-error', 'Write failed')
136+
it('should default to unknown values when no metadata provided', () => {
137+
telemetryModule([{ name: 'test', tags: [] }])
152138

153139
assertStdinMetadata({
154-
result: 'error',
155-
result_class: 'internal_error',
156-
result_reason: 'Failed to write telemetry data to telemetry forwarder'
140+
result: 'unknown',
141+
result_class: 'unknown',
142+
result_reason: 'unknown'
157143
})
158144
})
159145

160-
it('should set success metadata when telemetry forwarder exits successfully', () => {
161-
runTelemetry('exit', 0)
146+
it('should partially override default metadata', () => {
147+
telemetryModule('error', ['integration:mongodb'], {
148+
result: 'error',
149+
result_reason: 'Connection failed'
150+
})
162151

163152
assertStdinMetadata({
164-
result: 'success',
165-
result_class: 'success',
166-
result_reason: 'Successfully configured ddtrace package'
153+
result: 'error',
154+
result_class: 'unknown',
155+
result_reason: 'Connection failed'
167156
})
168157
})
169158
})

0 commit comments

Comments
 (0)