Skip to content

Commit

Permalink
add remote config support for custom tags (#3875)
Browse files Browse the repository at this point in the history
  • Loading branch information
rochdev authored Dec 21, 2023
1 parent 1c5e578 commit bcb71e1
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 131 deletions.
6 changes: 5 additions & 1 deletion packages/dd-trace/src/appsec/remote_config/capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@ module.exports = {
ASM_CUSTOM_RULES: 1n << 8n,
ASM_CUSTOM_BLOCKING_RESPONSE: 1n << 9n,
ASM_TRUSTED_IPS: 1n << 10n,
ASM_API_SECURITY_SAMPLE_RATE: 1n << 11n
ASM_API_SECURITY_SAMPLE_RATE: 1n << 11n,
APM_TRACING_SAMPLE_RATE: 1n << 12n,
APM_TRACING_LOGS_INJECTION: 1n << 13n,
APM_TRACING_HTTP_HEADER_TAGS: 1n << 14n,
APM_TRACING_CUSTOM_TAGS: 1n << 15n
}
4 changes: 4 additions & 0 deletions packages/dd-trace/src/appsec/remote_config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ let rc

function enable (config) {
rc = new RemoteConfigManager(config)
rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_CUSTOM_TAGS, true)
rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_HTTP_HEADER_TAGS, true)
rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_LOGS_INJECTION, true)
rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RATE, true)

const activation = Activation.fromConfig(config)

Expand Down
138 changes: 80 additions & 58 deletions packages/dd-trace/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,6 @@ class Config {
log.use(this.logger)
log.toggle(this.debug, this.logLevel, this)

this.tags = {}

tagger.add(this.tags, process.env.DD_TAGS)
tagger.add(this.tags, process.env.DD_TRACE_TAGS)
tagger.add(this.tags, process.env.DD_TRACE_GLOBAL_TAGS)
tagger.add(this.tags, options.tags)

const DD_TRACING_ENABLED = coalesce(
process.env.DD_TRACING_ENABLED,
true
Expand Down Expand Up @@ -184,33 +177,12 @@ class Config {
false
)

const DD_SERVICE = options.service ||
process.env.DD_SERVICE ||
process.env.DD_SERVICE_NAME ||
this.tags.service ||
process.env.AWS_LAMBDA_FUNCTION_NAME ||
process.env.FUNCTION_NAME || // Google Cloud Function Name set by deprecated runtimes
process.env.K_SERVICE || // Google Cloud Function Name set by newer runtimes
process.env.WEBSITE_SITE_NAME || // set by Azure Functions
pkg.name ||
'node'
const DD_SERVICE_MAPPING = coalesce(
options.serviceMapping,
process.env.DD_SERVICE_MAPPING ? fromEntries(
process.env.DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
) : {}
)
const DD_ENV = coalesce(
options.env,
process.env.DD_ENV,
this.tags.env
)
const DD_VERSION = coalesce(
options.version,
process.env.DD_VERSION,
this.tags.version,
pkg.version
)
const DD_TRACE_STARTUP_LOGS = coalesce(
options.startupLogs,
process.env.DD_TRACE_STARTUP_LOGS,
Expand Down Expand Up @@ -583,7 +555,6 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
this.dsmEnabled = isTrue(DD_DATA_STREAMS_ENABLED)
this.openAiLogsEnabled = DD_OPENAI_LOGS_ENABLED
this.apiKey = DD_API_KEY
this.env = DD_ENV
this.url = DD_CIVISIBILITY_AGENTLESS_URL ? new URL(DD_CIVISIBILITY_AGENTLESS_URL)
: getAgentUrl(DD_TRACE_AGENT_URL, options)
this.site = coalesce(options.site, process.env.DD_SITE, 'datadoghq.com')
Expand All @@ -595,9 +566,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
this.clientIpEnabled = DD_TRACE_CLIENT_IP_ENABLED
this.clientIpHeader = DD_TRACE_CLIENT_IP_HEADER
this.plugins = !!coalesce(options.plugins, true)
this.service = DD_SERVICE
this.serviceMapping = DD_SERVICE_MAPPING
this.version = DD_VERSION
this.dogstatsd = {
hostname: coalesce(dogstatsd.hostname, process.env.DD_DOGSTATSD_HOSTNAME, this.hostname),
port: String(coalesce(dogstatsd.port, process.env.DD_DOGSTATSD_PORT, 8125))
Expand Down Expand Up @@ -690,6 +659,31 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
// Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent
this.memcachedCommandEnabled = isTrue(DD_TRACE_MEMCACHED_COMMAND_ENABLED)

this.stats = {
enabled: isTrue(DD_TRACE_STATS_COMPUTATION_ENABLED)
}

this.traceId128BitGenerationEnabled = isTrue(DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED)
this.traceId128BitLoggingEnabled = isTrue(DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED)

this.isGCPFunction = isGCPFunction
this.isAzureFunctionConsumptionPlan = isAzureFunctionConsumptionPlan

this.spanLeakDebug = Number(DD_TRACE_SPAN_LEAK_DEBUG)

this._applyDefaults()
this._applyEnvironment()
this._applyOptions(options)
this._applyRemote({})
this._merge()

tagger.add(this.tags, {
service: this.service,
env: this.env,
version: this.version,
'runtime-id': uuid()
})

if (this.gitMetadataEnabled) {
this.repositoryUrl = removeUserSensitiveInfo(
coalesce(
Expand Down Expand Up @@ -722,31 +716,6 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
}
}
}

this.stats = {
enabled: isTrue(DD_TRACE_STATS_COMPUTATION_ENABLED)
}

this.traceId128BitGenerationEnabled = isTrue(DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED)
this.traceId128BitLoggingEnabled = isTrue(DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED)

this.isGCPFunction = isGCPFunction
this.isAzureFunctionConsumptionPlan = isAzureFunctionConsumptionPlan

this.spanLeakDebug = Number(DD_TRACE_SPAN_LEAK_DEBUG)

tagger.add(this.tags, {
service: this.service,
env: this.env,
version: this.version,
'runtime-id': uuid()
})

this._applyDefaults()
this._applyEnvironment()
this._applyOptions(options)
this._applyRemote({})
this._merge()
}

// Supports only a subset of options for now.
Expand All @@ -761,48 +730,93 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
}

_applyDefaults () {
const {
AWS_LAMBDA_FUNCTION_NAME,
FUNCTION_NAME,
K_SERVICE,
WEBSITE_SITE_NAME
} = process.env

const service = AWS_LAMBDA_FUNCTION_NAME ||
FUNCTION_NAME || // Google Cloud Function Name set by deprecated runtimes
K_SERVICE || // Google Cloud Function Name set by newer runtimes
WEBSITE_SITE_NAME || // set by Azure Functions
pkg.name ||
'node'

const defaults = this._defaults = {}

this._setValue(defaults, 'service', service)
this._setValue(defaults, 'env', undefined)
this._setValue(defaults, 'version', pkg.version)
this._setUnit(defaults, 'sampleRate', undefined)
this._setBoolean(defaults, 'logInjection', false)
this._setArray(defaults, 'headerTags', [])
this._setValue(defaults, 'tags', {})
}

_applyEnvironment () {
const {
DD_TRACE_SAMPLE_RATE,
DD_ENV,
DD_LOGS_INJECTION,
DD_TRACE_HEADER_TAGS
DD_SERVICE,
DD_SERVICE_NAME,
DD_TAGS,
DD_TRACE_GLOBAL_TAGS,
DD_TRACE_HEADER_TAGS,
DD_TRACE_SAMPLE_RATE,
DD_TRACE_TAGS,
DD_VERSION
} = process.env

const tags = {}
const env = this._env = {}

tagger.add(tags, DD_TAGS)
tagger.add(tags, DD_TRACE_TAGS)
tagger.add(tags, DD_TRACE_GLOBAL_TAGS)

this._setValue(env, 'service', DD_SERVICE || DD_SERVICE_NAME || tags.service)
this._setValue(env, 'env', DD_ENV || tags.env)
this._setValue(env, 'version', DD_VERSION || tags.version)
this._setUnit(env, 'sampleRate', DD_TRACE_SAMPLE_RATE)
this._setBoolean(env, 'logInjection', DD_LOGS_INJECTION)
this._setArray(env, 'headerTags', DD_TRACE_HEADER_TAGS)
this._setTags(env, 'tags', tags)
}

_applyOptions (options) {
const opts = this._options = this._options || {}
const tags = {}

options = Object.assign({ ingestion: {} }, options, opts)

tagger.add(tags, options.tags)

this._setValue(opts, 'service', options.service || tags.service)
this._setValue(opts, 'env', options.env || tags.env)
this._setValue(opts, 'version', options.version || tags.version)
this._setUnit(opts, 'sampleRate', coalesce(options.sampleRate, options.ingestion.sampleRate))
this._setBoolean(opts, 'logInjection', options.logInjection)
this._setArray(opts, 'headerTags', options.headerTags)
this._setTags(opts, 'tags', tags)
}

_applyRemote (options) {
const opts = this._remote = this._remote || {}
const tags = {}
const headerTags = options.tracing_header_tags
? options.tracing_header_tags.map(tag => {
return tag.tag_name ? `${tag.header}:${tag.tag_name}` : tag.header
})
: undefined

tagger.add(tags, options.tracing_tags)

this._setUnit(opts, 'sampleRate', options.tracing_sampling_rate)
this._setBoolean(opts, 'logInjection', options.log_injection_enabled)
this._setArray(opts, 'headerTags', headerTags)
this._setTags(opts, 'tags', tags)
}

_setBoolean (obj, name, value) {
Expand Down Expand Up @@ -842,6 +856,14 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
}
}

_setTags (obj, name, value) {
if (!value || Object.keys(value).length === 0) {
return this._setValue(obj, name, null)
}

this._setValue(obj, name, value)
}

_setValue (obj, name, value) {
obj[name] = value
}
Expand Down
4 changes: 2 additions & 2 deletions packages/dd-trace/src/opentracing/tracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class DatadogTracer {
constructor (config) {
const Exporter = getExporter(config.experimental.exporter)

this._config = config
this._service = config.service
this._version = config.version
this._env = config.env
this._tags = config.tags
this._logInjection = config.logInjection
this._debug = config.debug
this._prioritySampler = new PrioritySampler(config.env, config.sampler)
Expand Down Expand Up @@ -64,7 +64,7 @@ class DatadogTracer {
integrationName: options.integrationName
}, this._debug)

span.addTags(this._tags)
span.addTags(this._config.tags)
span.addTags(options.tags)

return span
Expand Down
29 changes: 24 additions & 5 deletions packages/dd-trace/src/telemetry/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,30 @@ function updateConfig (changes, config) {
const application = createAppObject(config)
const host = createHostObject()

const configuration = changes.map(change => ({
name: change.name,
value: Array.isArray(change.value) ? change.value.join(',') : change.value,
origin: change.origin
}))
const names = {
sampleRate: 'DD_TRACE_SAMPLE_RATE',
logInjection: 'DD_LOG_INJECTION',
headerTags: 'DD_TRACE_HEADER_TAGS',
tags: 'DD_TAGS'
}

const configuration = []

for (const change of changes) {
if (!names.hasOwnProperty(change.name)) continue

const name = names[change.name]
const { origin, value } = change
const entry = { name, origin, value }

if (Array.isArray(value)) {
entry.value = value.join(',')
} else if (name === 'DD_TAGS') {
entry.value = Object.entries(value).map(([key, value]) => `${key}:${value}`)
}

configuration.push(entry)
}

const { reqType, payload } = createPayload('app-client-configuration-change', { configuration })

Expand Down
Loading

0 comments on commit bcb71e1

Please sign in to comment.