Skip to content

Commit dcde0e0

Browse files
authored
feat(process-tags): Propagate Service Naming Context via Process Tags (#7762)
* initial work for service naming process tag * reworked process tags implementation * lint fix * removed testing leftovers * fix grammar mistake * fix test being undefined * added missing inits * debugger fix
1 parent 13dc1b2 commit dcde0e0

File tree

14 files changed

+148
-41
lines changed

14 files changed

+148
-41
lines changed

integration-tests/debugger/diagnostics.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ const { pollInterval, setup } = require('./utils')
88
describe('Dynamic Instrumentation', function () {
99
const t = setup({ testApp: 'target-app/basic.js', dependencies: ['fastify'] })
1010

11+
before(function () {
12+
require('../../packages/dd-trace/src/process-tags').initialize()
13+
})
14+
1115
describe('diagnostics messages', function () {
1216
it('should send expected diagnostics messages if probe is received and triggered', function (done) {
1317
let receivedAckUpdate = false

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ const defaultsWithoutSupportedConfigurationEntry = {
106106
isGCPFunction: false,
107107
instrumentationSource: 'manual',
108108
isServiceUserProvided: false,
109+
isServiceNameInferred: true,
109110
lookup: undefined,
110111
plugins: true,
111112
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,8 +722,10 @@ class Config {
722722
// Priority:
723723
// DD_SERVICE > tags.service > OTEL_SERVICE_NAME > NX_TASK_TARGET_PROJECT (if DD_ENABLE_NX_SERVICE_NAME) > default
724724
let serviceName = DD_SERVICE || tags.service || OTEL_SERVICE_NAME
725+
let isServiceNameInferred
725726
if (!serviceName && NX_TASK_TARGET_PROJECT) {
726727
if (isTrue(DD_ENABLE_NX_SERVICE_NAME)) {
728+
isServiceNameInferred = true
727729
serviceName = NX_TASK_TARGET_PROJECT
728730
} else if (DD_MAJOR < 6) {
729731
// Warn about v6 behavior change for Nx projects
@@ -734,6 +736,7 @@ class Config {
734736
}
735737
}
736738
setString(target, 'service', serviceName)
739+
if (serviceName) setBoolean(target, 'isServiceNameInferred', isServiceNameInferred ?? false)
737740
if (DD_SERVICE_MAPPING) {
738741
target.serviceMapping = Object.fromEntries(
739742
DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
@@ -1004,7 +1007,11 @@ class Config {
10041007
setUnit(opts, 'sampleRate', options.sampleRate ?? options.ingestion.sampleRate)
10051008
opts['sampler.rateLimit'] = maybeInt(options.rateLimit ?? options.ingestion.rateLimit)
10061009
setSamplingRule(opts, 'sampler.rules', options.samplingRules)
1007-
setString(opts, 'service', options.service || tags.service)
1010+
const optService = options.service || tags.service
1011+
setString(opts, 'service', optService)
1012+
if (optService) {
1013+
setBoolean(opts, 'isServiceNameInferred', false)
1014+
}
10081015
opts.serviceMapping = options.serviceMapping
10091016
setString(opts, 'site', options.site)
10101017
if (options.spanAttributeSchema) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
const { workerData: { config: parentConfig, parentThreadId, configPort } } = require('node:worker_threads')
44
const { getAgentUrl } = require('../../agent/url')
5+
const processTags = require('../../process-tags')
56
const log = require('./log')
67

8+
processTags.initialize()
9+
710
const config = module.exports = {
811
...parentConfig,
912
parentThreadId,

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

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,19 @@ const ENTRYPOINT_PATH = require.main?.filename || ''
1212
// entrypoint.basedir = baz
1313
// package.json.name = <from package.json>
1414

15-
// process tags are constant throughout the lifetime of a process
16-
function getProcessTags () {
15+
/**
16+
* Sanitize a process tag value
17+
*
18+
* @param {string} value
19+
* @returns {string}
20+
*/
21+
function sanitize (value) {
22+
return String(value)
23+
.toLowerCase()
24+
.replaceAll(/[^a-zA-Z0-9/_.-]+/g, '_')
25+
}
26+
27+
function buildProcessTags (config) {
1728
// Lazy load pkg to avoid issues with require.main during test initialization
1829
const pkg = require('../pkg')
1930

@@ -35,6 +46,13 @@ function getProcessTags () {
3546
['package.json.name', pkg.name || undefined],
3647
]
3748

49+
// If config dependent tags keep growing, we should consider moving this into a function
50+
if (config?.isServiceNameInferred) {
51+
tags.push(['svc.auto', config.service])
52+
} else if (config) {
53+
tags.push(['svc.user', true])
54+
}
55+
3856
const tagsArray = []
3957
const tagsObject = {}
4058

@@ -46,38 +64,27 @@ function getProcessTags () {
4664
}
4765
}
4866

49-
const serialized = tagsArray.join(',')
50-
51-
return {
52-
tags,
53-
serialized,
54-
tagsObject,
55-
tagsArray,
56-
}
67+
processTags.tags = tags
68+
processTags.serialized = tagsArray.join(',')
69+
processTags.tagsObject = tagsObject
70+
processTags.tagsArray = tagsArray
5771
}
5872

59-
// Export the singleton
60-
module.exports = getProcessTags()
61-
62-
module.exports.TRACING_FIELD_NAME = '_dd.tags.process'
63-
module.exports.DSM_FIELD_NAME = 'ProcessTags'
64-
module.exports.PROFILING_FIELD_NAME = 'process_tags'
65-
module.exports.DYNAMIC_INSTRUMENTATION_FIELD_NAME = 'process_tags'
66-
module.exports.TELEMETRY_FIELD_NAME = 'process_tags'
67-
module.exports.REMOTE_CONFIG_FIELD_NAME = 'process_tags'
68-
module.exports.CRASH_TRACKING_FIELD_NAME = 'process_tags'
69-
module.exports.CLIENT_TRACE_STATISTICS_FIELD_NAME = 'ProcessTags'
70-
71-
/**
72-
* Sanitize a process tag value
73-
*
74-
* @param {string} value
75-
* @returns {string}
76-
*/
77-
function sanitize (value) {
78-
return String(value)
79-
.toLowerCase()
80-
.replaceAll(/[^a-zA-Z0-9/_.-]+/g, '_')
73+
// Singleton with constant defaults so pre-init reads don't blow up
74+
const processTags = module.exports = {
75+
initialize (config) {
76+
// check if one of the properties added during build exist and if so return
77+
if (processTags.tags) return
78+
buildProcessTags(config)
79+
},
80+
81+
TRACING_FIELD_NAME: '_dd.tags.process',
82+
DSM_FIELD_NAME: 'ProcessTags',
83+
PROFILING_FIELD_NAME: 'process_tags',
84+
DYNAMIC_INSTRUMENTATION_FIELD_NAME: 'process_tags',
85+
TELEMETRY_FIELD_NAME: 'process_tags',
86+
REMOTE_CONFIG_FIELD_NAME: 'process_tags',
87+
CRASH_TRACKING_FIELD_NAME: 'process_tags',
88+
CLIENT_TRACE_STATISTICS_FIELD_NAME: 'ProcessTags',
89+
sanitize,
8190
}
82-
83-
module.exports.sanitize = sanitize

packages/dd-trace/src/proxy.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const nomenclature = require('./service-naming')
1313
const PluginManager = require('./plugin_manager')
1414
const NoopDogStatsDClient = require('./noop/dogstatsd')
1515
const { IS_SERVERLESS } = require('./serverless')
16+
const processTags = require('./process-tags')
1617
const {
1718
setBaggageItem,
1819
getBaggageItem,
@@ -102,6 +103,9 @@ class Tracer extends NoopProxy {
102103
try {
103104
const config = getConfig(options) // TODO: support dynamic code config
104105

106+
// Add config dependent process tags
107+
processTags.initialize(config)
108+
105109
// Configure propagation hash manager for process tags + container tags
106110
const propagationHash = require('./propagation-hash')
107111
propagationHash.configure(config)

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2993,7 +2993,7 @@ describe('Config', () => {
29932993

29942994
delete process.env.DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH
29952995

2996-
;({ cloudPayloadTagging } = getConfig({ cloudPayloadTagging: { maxDepth: 7 } }))
2996+
; ({ cloudPayloadTagging } = getConfig({ cloudPayloadTagging: { maxDepth: 7 } }))
29972997
assertObjectContains(cloudPayloadTagging, {
29982998
maxDepth: 7,
29992999
requestsEnabled: true,
@@ -3011,7 +3011,7 @@ describe('Config', () => {
30113011

30123012
delete process.env.DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH
30133013

3014-
;({ cloudPayloadTagging } = getConfig({ cloudPayloadTagging: { maxDepth: NaN } }))
3014+
; ({ cloudPayloadTagging } = getConfig({ cloudPayloadTagging: { maxDepth: NaN } }))
30153015
assertObjectContains(cloudPayloadTagging, {
30163016
maxDepth: 10,
30173017
})
@@ -3876,4 +3876,38 @@ rules:
38763876
assert.notStrictEqual(config.sampler.rateLimit, -1)
38773877
})
38783878
})
3879+
3880+
describe('should detect when service name is inferred', () => {
3881+
it('should set isServiceNameInferred to false when DD_SERVICE is defined ', () => {
3882+
process.env.DD_SERVICE = 'test-service'
3883+
const config = getConfig()
3884+
assert.strictEqual(config.isServiceNameInferred, false)
3885+
assert.strictEqual(config.service, 'test-service')
3886+
})
3887+
3888+
it('should set isServiceNameInferred to false when options.service is defined', () => {
3889+
const config = getConfig({ service: 'test-service-option' })
3890+
assert.strictEqual(config.isServiceNameInferred, false)
3891+
assert.strictEqual(config.service, 'test-service-option')
3892+
})
3893+
3894+
it('should set isServiceNameInferred to false when OTEL_SERVICE_NAME is defined', () => {
3895+
process.env.OTEL_SERVICE_NAME = 'test-service-otel'
3896+
const config = getConfig()
3897+
assert.strictEqual(config.isServiceNameInferred, false)
3898+
assert.strictEqual(config.service, 'test-service-otel')
3899+
})
3900+
3901+
it('should set isServiceNameInferred to false when tags.service is defined', () => {
3902+
const config = getConfig({ tags: { service: 'test-service-tags' } })
3903+
assert.strictEqual(config.isServiceNameInferred, false)
3904+
assert.strictEqual(config.service, 'test-service-tags')
3905+
})
3906+
3907+
it('should set isServiceNameInfered to true when no name is given', () => {
3908+
const config = getConfig()
3909+
assert.strictEqual(config.isServiceNameInferred, true)
3910+
assert.strictEqual(config.service, 'node')
3911+
})
3912+
})
38793913
})

packages/dd-trace/test/crashtracking/crashtracker.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ describeNotWindows('crashtracker', () => {
1616
let libdatadog
1717
let log
1818

19+
before(() => {
20+
require('../../src/process-tags').initialize()
21+
})
22+
1923
beforeEach(() => {
2024
libdatadog = require('@datadog/libdatadog')
2125

packages/dd-trace/test/datastreams/processor.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ describe('DataStreamsProcessor', () => {
319319
it('should include ProcessTags when propagation is enabled', () => {
320320
const propagationHash = require('../../src/propagation-hash')
321321
const processTags = require('../../src/process-tags')
322+
processTags.initialize()
322323

323324
// Configure and enable the feature
324325
propagationHash.configure({ propagateProcessTags: { enabled: true } })

packages/dd-trace/test/encode/span-stats.spec.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ describe('span-stats-encode', () => {
2828
let stat
2929

3030
beforeEach(() => {
31+
processTags.initialize()
32+
3133
logger = {
3234
debug: sinon.stub(),
3335
}

0 commit comments

Comments
 (0)