Skip to content

Commit ac05a9e

Browse files
committed
[DI] add process tags to dynamic instrumentation
1 parent edfc7c6 commit ac05a9e

File tree

8 files changed

+154
-48
lines changed

8 files changed

+154
-48
lines changed

integration-tests/debugger/basic.spec.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,58 @@ describe('Dynamic Instrumentation', function () {
800800
)
801801
})
802802
})
803+
804+
describe('DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=true', function () {
805+
const t = setup({
806+
env: { DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED: 'true' },
807+
dependencies: ['fastify']
808+
})
809+
810+
describe('input messages', function () {
811+
it('should include process_tags in snapshot when enabled', function (done) {
812+
t.agent.on('debugger-input', ({ payload }) => {
813+
const snapshot = payload[0].debugger.snapshot
814+
815+
// Assert that process_tags are present
816+
assert.ok(snapshot.process_tags)
817+
assert.strictEqual(typeof snapshot.process_tags, 'object')
818+
819+
// Check for expected process tags keys
820+
assert.ok(snapshot.process_tags['entrypoint.name'])
821+
assert.ok(snapshot.process_tags['entrypoint.type'])
822+
assert.strictEqual(snapshot.process_tags['entrypoint.type'], 'script')
823+
824+
done()
825+
})
826+
827+
t.triggerBreakpoint()
828+
t.agent.addRemoteConfig(t.rcConfig)
829+
})
830+
})
831+
})
832+
833+
describe('DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=false', function () {
834+
const t = setup({
835+
env: { DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED: 'false' },
836+
dependencies: ['fastify']
837+
})
838+
839+
describe('input messages', function () {
840+
it('should not include process_tags in snapshot when disabled', function (done) {
841+
t.agent.on('debugger-input', ({ payload }) => {
842+
const snapshot = payload[0].debugger.snapshot
843+
844+
// Assert that process_tags are not present
845+
assert.strictEqual(snapshot.process_tags, undefined)
846+
847+
done()
848+
})
849+
850+
t.triggerBreakpoint()
851+
t.agent.addRemoteConfig(t.rcConfig)
852+
})
853+
})
854+
})
803855
})
804856

805857
function testBasicInput (t, done) {

packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const {
44
workerData: {
5+
config,
56
breakpointSetChannel,
67
breakpointHitChannel,
78
breakpointRemoveChannel
@@ -21,6 +22,7 @@ const {
2122
getStackFromCallFrames
2223
} = require('../../../debugger/devtools_client/state')
2324
const log = require('../../../log')
25+
const processTags = require('../../../process-tags')
2426

2527
let sessionStarted = false
2628

@@ -55,6 +57,10 @@ session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint]
5557
language: 'javascript'
5658
}
5759

60+
if (config.propagateProcessTags?.enabled) {
61+
snapshot[processTags.DYNAMIC_INSTRUMENTATION_FIELD_NAME] = processTags.tagsObject
62+
}
63+
5864
breakpointHitChannel.postMessage({ snapshot })
5965
})
6066

packages/dd-trace/src/debugger/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = function getDebuggerConfig (config) {
88
hostname: config.hostname,
99
logLevel: config.logLevel,
1010
port: config.port,
11+
propagateProcessTags: config.propagateProcessTags,
1112
repositoryUrl: config.repositoryUrl,
1213
runtimeId: config.tags['runtime-id'],
1314
service: config.service,

packages/dd-trace/src/debugger/devtools_client/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const { MAX_SNAPSHOTS_PER_SECOND_GLOBALLY } = require('./defaults')
1212
const log = require('./log')
1313
const { version } = require('../../../../../package.json')
1414
const { NODE_MAJOR } = require('../../../../../version')
15+
const processTags = require('../../process-tags')
1516

1617
require('./remote_config')
1718

@@ -215,6 +216,10 @@ session.on('Debugger.paused', async ({ params }) => {
215216
language: 'javascript'
216217
}
217218

219+
if (config.propagateProcessTags?.enabled) {
220+
snapshot[processTags.DYNAMIC_INSTRUMENTATION_FIELD_NAME] = processTags.tagsObject
221+
}
222+
218223
if (probe.captureSnapshot) {
219224
if (captureErrors?.length > 0) {
220225
// There was an error collecting the snapshot for this probe, let's not try again

packages/dd-trace/src/process-tags/index.js

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,22 @@
11
'use strict'
22

33
const path = require('node:path')
4-
const pkg = require('../pkg')
54

65
const CURRENT_WORKING_DIRECTORY = process.cwd()
76
const ENTRYPOINT_PATH = require.main?.filename || ''
87

9-
const TRACING_FIELD_NAME = '_dd.tags.process'
10-
const DSM_FIELD_NAME = 'ProcessTags'
11-
const PROFILING_FIELD_NAME = 'process_tags'
12-
13-
module.exports.TRACING_FIELD_NAME = TRACING_FIELD_NAME
14-
module.exports.DSM_FIELD_NAME = DSM_FIELD_NAME
15-
module.exports.PROFILING_FIELD_NAME = PROFILING_FIELD_NAME
16-
17-
// TODO CRASH_TRACKING_FIELD_NAME /process_tags /application/process_tags
18-
// TODO: TELEMETRY_FIELD_NAME /application/process_tags
19-
// TODO: DYNAMIC_INSTRUMENTATION_FIELD_NAME process_tags
20-
// TODO: CLIENT_TRACE_STATISTICS_FIELD_NAME process_tags
21-
// TODO: REMOTE_CONFIG_FIELD_NAME process_tags
22-
238
// $ cd /foo/bar && node baz/banana.js
249
// entrypoint.workdir = bar
2510
// entrypoint.name = banana
2611
// entrypoint.type = script
2712
// entrypoint.basedir = baz
2813
// package.json.name = <from package.json>
2914

30-
module.exports = function getProcessTags () {
15+
// process tags are constant throughout the lifetime of a process
16+
function getProcessTags () {
17+
// Lazy load pkg to avoid issues with require.main during test initialization
18+
const pkg = require('../pkg')
19+
3120
// this list is sorted alphabetically for consistent serialization
3221
const tags = [
3322
// the parent directory name of the entrypoint script, e.g. /foo/bar/baz/banana.js -> baz
@@ -48,16 +37,36 @@ module.exports = function getProcessTags () {
4837

4938
const serialized = serialize(tags)
5039

40+
const tagsObject = tags.reduce((acc, [key, value]) => {
41+
if (value !== undefined) {
42+
acc[key] = value
43+
}
44+
return acc
45+
}, {})
46+
5147
return {
5248
tags,
53-
serialized
49+
serialized,
50+
tagsObject
5451
}
5552
}
5653

54+
// Export the singleton
55+
module.exports = getProcessTags()
56+
57+
module.exports.TRACING_FIELD_NAME = '_dd.tags.process'
58+
module.exports.DSM_FIELD_NAME = 'ProcessTags'
59+
module.exports.PROFILING_FIELD_NAME = 'process_tags'
60+
module.exports.DYNAMIC_INSTRUMENTATION_FIELD_NAME = 'process_tags'
61+
62+
// TODO: CRASH_TRACKING_FIELD_NAME /process_tags /application/process_tags
63+
// TODO: TELEMETRY_FIELD_NAME /application/process_tags
64+
// TODO: CLIENT_TRACE_STATISTICS_FIELD_NAME process_tags
65+
// TODO: REMOTE_CONFIG_FIELD_NAME process_tags
66+
5767
function serialize (tags) {
5868
const intermediary = []
5969
for (const [name, value] of tags) {
60-
// if we don't have a value we should send nothing at all
6170
if (value === undefined) continue
6271
intermediary.push(`${name}:${sanitize(value)}`)
6372
}

packages/dd-trace/src/span_processor.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const spanFormat = require('./span_format')
55
const SpanSampler = require('./span_sampler')
66
const GitMetadataTagger = require('./git_metadata_tagger')
77
const { getEnvironmentVariable } = require('./config-helper')
8-
const getProcessTags = require('./process-tags')
8+
const processTags = require('./process-tags')
99

1010
const startedSpans = new WeakSet()
1111
const finishedSpans = new WeakSet()
@@ -27,7 +27,7 @@ class SpanProcessor {
2727
this._gitMetadataTagger = new GitMetadataTagger(config)
2828

2929
this._processTags = config.propagateProcessTags?.enabled
30-
? getProcessTags().serialized
30+
? processTags.serialized
3131
: false
3232
}
3333

packages/dd-trace/test/debugger/config.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('getDebuggerConfig', function () {
2121
'hostname',
2222
'logLevel',
2323
'port',
24+
'propagateProcessTags',
2425
'repositoryUrl',
2526
'runtimeId',
2627
'service',

packages/dd-trace/test/process-tags.spec.js

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,34 @@ const { describe, it, beforeEach, afterEach } = require('tap').mocha
77
require('./setup/core')
88

99
describe('process-tags', () => {
10-
const getProcessTags = require('../src/process-tags')
10+
const processTags = require('../src/process-tags')
1111
const { serialize, sanitize } = require('../src/process-tags')
1212

13-
describe('getProcessTags', () => {
14-
it('should return an object with tags and serialized properties', () => {
15-
const result = getProcessTags()
13+
describe('processTags', () => {
14+
it('should return an object with tags, serialized, and tagsObject properties', () => {
15+
assert.ok(Object.hasOwn(processTags, 'tags'))
16+
assert.ok(Object.hasOwn(processTags, 'serialized'))
17+
assert.ok(Object.hasOwn(processTags, 'tagsObject'))
18+
assert.ok(Array.isArray(processTags.tags))
19+
assert.strictEqual(typeof processTags.serialized, 'string')
20+
assert.strictEqual(typeof processTags.tagsObject, 'object')
21+
})
22+
23+
it('should have tagsObject with only defined values', () => {
24+
const { tagsObject } = processTags
25+
26+
// All values in tagsObject should be defined
27+
Object.values(tagsObject).forEach(value => {
28+
assert.notStrictEqual(value, undefined)
29+
})
1630

17-
assert.ok(Object.hasOwn(result, 'tags'))
18-
assert.ok(Object.hasOwn(result, 'serialized'))
19-
assert.ok(Array.isArray(result.tags))
20-
assert.strictEqual(typeof result.serialized, 'string')
31+
// tagsObject should have the same keys as defined tags
32+
const definedTags = processTags.tags.filter(([, value]) => value !== undefined)
33+
assert.strictEqual(Object.keys(tagsObject).length, definedTags.length)
2134
})
2235

2336
it('should include all expected tag names', () => {
24-
const result = getProcessTags()
25-
const tagNames = result.tags.map(([name]) => name).sort()
37+
const tagNames = processTags.tags.map(([name]) => name)
2638

2739
assertObjectContains(
2840
tagNames,
@@ -37,41 +49,56 @@ describe('process-tags', () => {
3749
})
3850

3951
it('should have entrypoint.type set to "script"', () => {
40-
const result = getProcessTags()
41-
const typeTag = result.tags.find(([name]) => name === 'entrypoint.type')
52+
const typeTag = processTags.tags.find(([name]) => name === 'entrypoint.type')
4253

4354
assert.ok(Array.isArray(typeTag))
4455
assert.strictEqual(typeTag[1], 'script')
4556
})
4657

4758
it('should set entrypoint.workdir to the basename of cwd', () => {
48-
const result = getProcessTags()
49-
const workdirTag = result.tags.find(([name]) => name === 'entrypoint.workdir')
59+
const workdirTag = processTags.tags.find(([name]) => name === 'entrypoint.workdir')
5060

5161
assert.ok(Array.isArray(workdirTag))
5262
assert.strictEqual(typeof workdirTag[1], 'string')
5363
assert.doesNotMatch(workdirTag[1], /\//)
5464
})
5565

56-
// note that these tests may fail if the tracer folder structure changes
57-
it('should set sensible values based on tracer project structure and be sorted alphabetically', () => {
58-
const result = getProcessTags()
66+
it('should set sensible values', () => {
67+
const basedirTag = processTags.tags.find(([name]) => name === 'entrypoint.basedir')
68+
const nameTag = processTags.tags.find(([name]) => name === 'entrypoint.name')
69+
const typeTag = processTags.tags.find(([name]) => name === 'entrypoint.type')
70+
const workdirTag = processTags.tags.find(([name]) => name === 'entrypoint.workdir')
71+
const packageNameTag = processTags.tags.find(([name]) => name === 'package.json.name')
72+
73+
// Entrypoint values should be set (may vary depending on test runner)
74+
assert.ok(basedirTag)
75+
assert.strictEqual(typeof basedirTag[1], 'string')
76+
assert.ok(nameTag)
77+
assert.strictEqual(typeof nameTag[1], 'string')
78+
79+
assert.ok(typeTag)
80+
assert.strictEqual(typeTag[1], 'script')
81+
82+
assert.ok(workdirTag)
83+
assert.strictEqual(workdirTag[1], 'dd-trace-js')
5984

60-
assert.deepStrictEqual(result.tags, [
61-
['entrypoint.basedir', 'test'],
62-
['entrypoint.name', 'process-tags.spec'],
63-
['entrypoint.type', 'script'],
64-
['entrypoint.workdir', 'dd-trace-js'],
65-
['package.json.name', 'dd-trace'],
66-
])
85+
// Package name should exist but may vary depending on test runner
86+
assert.ok(packageNameTag)
87+
assert.strictEqual(typeof packageNameTag[1], 'string')
6788
})
6889

69-
it('should serialize tags correctly', () => {
70-
const result = getProcessTags()
90+
it('should sort tags alphabetically', () => {
91+
assert.strictEqual(processTags.tags[0][0], 'entrypoint.basedir')
92+
assert.strictEqual(processTags.tags[1][0], 'entrypoint.name')
93+
assert.strictEqual(processTags.tags[2][0], 'entrypoint.type')
94+
assert.strictEqual(processTags.tags[3][0], 'entrypoint.workdir')
95+
assert.strictEqual(processTags.tags[4][0], 'package.json.name')
96+
})
7197

98+
it('should serialize tags correctly', () => {
7299
// serialized should be comma-separated and not include undefined values
73-
if (result.serialized) {
74-
const parts = result.serialized.split(',')
100+
if (processTags.serialized) {
101+
const parts = processTags.serialized.split(',')
75102
assert.ok(parts.length > 0)
76103
parts.forEach(part => {
77104
assert.match(part, /:/)
@@ -280,11 +307,16 @@ describe('process-tags', () => {
280307
process.env = env
281308
delete require.cache[require.resolve('../src/config')]
282309
delete require.cache[require.resolve('../src/span_processor')]
310+
delete require.cache[require.resolve('../src/process-tags')]
283311
})
284312

285313
it('should enable process tags propagation when set to true', () => {
286314
process.env.DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED = 'true'
287315

316+
// Need to reload config first, then process-tags (which reads from config)
317+
delete require.cache[require.resolve('../src/config')]
318+
delete require.cache[require.resolve('../src/process-tags')]
319+
288320
getConfig = require('../src/config')
289321
const config = getConfig()
290322

0 commit comments

Comments
 (0)