Skip to content

Commit

Permalink
upgrade(tracer): Support libdatadog's library_config module
Browse files Browse the repository at this point in the history
  • Loading branch information
BaptisteFoy committed Feb 18, 2025
1 parent dc57b5a commit 2bcf68f
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 10 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"node": ">=18"
},
"dependencies": {
"@datadog/libdatadog": "^0.4.0",
"@datadog/libdatadog": "^0.5.0",
"@datadog/native-appsec": "8.4.0",
"@datadog/native-iast-rewriter": "2.8.0",
"@datadog/native-iast-taint-tracking": "3.2.0",
Expand Down
134 changes: 129 additions & 5 deletions packages/dd-trace/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const telemetryMetrics = require('./telemetry/metrics')
const { getIsGCPFunction, getIsAzureFunction } = require('./serverless')
const { ORIGIN_KEY, GRPC_CLIENT_ERROR_STATUSES, GRPC_SERVER_ERROR_STATUSES } = require('./constants')
const { appendRules } = require('./payload-tagging/config')
const libdatadog = require('@datadog/libdatadog')

const tracerMetrics = telemetryMetrics.manager.namespace('tracers')

Expand Down Expand Up @@ -236,6 +237,8 @@ function reformatSpanSamplingRules (rules) {

class Config {
constructor (options = {}) {
const { localFileConfigEntries, fleetFileConfigEntries } = this.getStableConfig()

options = {
...options,
appsec: options.appsec != null ? options.appsec : options.experimental?.appsec,
Expand All @@ -244,9 +247,22 @@ class Config {

// Configure the logger first so it can be used to warn about other configs
const logConfig = log.getConfig()
this.debug = logConfig.enabled
this.debug = isTrue(coalesce(
fleetFileConfigEntries.DD_TRACE_DEBUG,
process.env.DD_TRACE_DEBUG,
process.env.OTEL_LOG_LEVEL === 'debug' || undefined,
localFileConfigEntries.DD_TRACE_DEBUG,
logConfig.enabled
))
this.logger = coalesce(options.logger, logConfig.logger)
this.logLevel = coalesce(options.logLevel, logConfig.logLevel)
this.logLevel = coalesce(
options.logLevel,
fleetFileConfigEntries.DD_TRACE_LOG_LEVEL,
process.env.DD_TRACE_LOG_LEVEL,
process.env.OTEL_LOG_LEVEL,
localFileConfigEntries.DD_TRACE_LOG_LEVEL,
logConfig.logLevel
)

log.use(this.logger)
log.toggle(this.debug, this.logLevel)
Expand Down Expand Up @@ -337,7 +353,9 @@ class Config {
}

this._applyDefaults()
this._applyLocalStableConfig(localFileConfigEntries)
this._applyEnvironment()
this._applyFleetStableConfig(fleetFileConfigEntries)
this._applyOptions(options)
this._applyCalculated()
this._applyRemote({})
Expand Down Expand Up @@ -390,6 +408,44 @@ class Config {
}
}

getStableConfig () {
// Note: we use maybeLoad because there may be cases where the library is not available and we
// want to avoid breaking the application. In those cases, we will not have the file-based configuration.
const libconfig = libdatadog.maybeLoad('library_config')
const localFileConfigEntries = {}
const fleetFileConfigEntries = {}
if (libconfig != null) {
const configurator = new libconfig.JsConfigurator()

const localConfigPath = process.env.DD_TEST_LOCAL_CONFIG_PATH ??
configurator.get_config_local_path(process.platform)
const fleetConfigPath = process.env.DD_TEST_FLEET_CONFIG_PATH ??
configurator.get_config_managed_path(process.platform)

let localConfig = ''
try {
localConfig = fs.readFileSync(localConfigPath, 'utf8')
} catch (err) {}
let fleetConfig = ''
try {
fleetConfig = fs.readFileSync(fleetConfigPath, 'utf8')
} catch (err) {}

if (localConfig || fleetConfig) {
configurator.set_envp(Object.entries(process.env).map(([key, value]) => `${key}=${value}`))
configurator.set_args(process.argv)
configurator.get_configuration(localConfig.toString(), fleetConfig.toString()).forEach((entry) => {
if (entry.source === 'local_stable_config') {
localFileConfigEntries[entry.name] = entry.value
} else if (entry.source === 'fleet_stable_config') {
fleetFileConfigEntries[entry.name] = entry.value
}
})
}
}
return { localFileConfigEntries, fleetFileConfigEntries }
}

// Supports only a subset of options for now.
configure (options, remote) {
if (remote) {
Expand Down Expand Up @@ -575,6 +631,50 @@ class Config {
this._setValue(defaults, 'aws.dynamoDb.tablePrimaryKeys', undefined)
}

_applyLocalStableConfig (localFileConfigEntries) {
const obj = setHiddenProperty(this, '_localStableConfig', {})
this._applyStableConfig(localFileConfigEntries, obj)
}

_applyFleetStableConfig (fleetFileConfigEntries) {
const obj = setHiddenProperty(this, '_fleetStableConfig', {})
this._applyStableConfig(fleetFileConfigEntries, obj)
}

_applyStableConfig (config, obj) {
const {
DD_APPSEC_ENABLED,
DD_APPSEC_SCA_ENABLED,
DD_DATA_STREAMS_ENABLED,
DD_DYNAMIC_INSTRUMENTATION_ENABLED,
DD_ENV,
DD_IAST_ENABLED,
DD_LOGS_INJECTION,
DD_PROFILING_ENABLED,
DD_RUNTIME_METRICS_ENABLED,
DD_SERVICE,
DD_VERSION
} = config

this._setBoolean(obj, 'appsec.enabled', DD_APPSEC_ENABLED)
this._setBoolean(obj, 'appsec.sca.enabled', DD_APPSEC_SCA_ENABLED)
this._setBoolean(obj, 'dsmEnabled', DD_DATA_STREAMS_ENABLED)
this._setBoolean(obj, 'dynamicInstrumentation.enabled', DD_DYNAMIC_INSTRUMENTATION_ENABLED)
this._setString(obj, 'env', DD_ENV)
this._setBoolean(obj, 'iast.enabled', DD_IAST_ENABLED)
this._setBoolean(obj, 'logInjection', DD_LOGS_INJECTION)
const profilingEnabledEnv = DD_PROFILING_ENABLED
const profilingEnabled = isTrue(profilingEnabledEnv)
? 'true'
: isFalse(profilingEnabledEnv)
? 'false'
: profilingEnabledEnv === 'auto' ? 'auto' : undefined
this._setString(obj, 'profiling.enabled', profilingEnabled)
this._setBoolean(obj, 'runtimeMetrics', DD_RUNTIME_METRICS_ENABLED)
this._setString(obj, 'service', DD_SERVICE)
this._setString(obj, 'version', DD_VERSION)
}

_applyEnvironment () {
const {
AWS_LAMBDA_FUNCTION_NAME,
Expand Down Expand Up @@ -1317,9 +1417,33 @@ class Config {
// eslint-disable-next-line @stylistic/js/max-len
// https://github.com/DataDog/dd-go/blob/prod/trace/apps/tracer-telemetry-intake/telemetry-payload/static/config_norm_rules.json
_merge () {
const containers = [this._remote, this._options, this._env, this._calculated, this._defaults]
const origins = ['remote_config', 'code', 'env_var', 'calculated', 'default']
const unprocessedValues = [this._remoteUnprocessed, this._optsUnprocessed, this._envUnprocessed, {}, {}]
const containers = [
this._remote,
this._options,
this._fleetStableConfig,
this._env,
this._localStableConfig,
this._calculated,
this._defaults
]
const origins = [
'remote_config',
'code',
'fleet_stable_config',
'env_var',
'local_stable_config',
'calculated',
'default'
]
const unprocessedValues = [
this._remoteUnprocessed,
this._optsUnprocessed,
{},
this._envUnprocessed,
{},
{},
{}
]
const changes = []

for (const name in this._defaults) {
Expand Down
81 changes: 81 additions & 0 deletions packages/dd-trace/test/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { expect } = require('chai')
const { readFileSync } = require('fs')
const sinon = require('sinon')
const { GRPC_CLIENT_ERROR_STATUSES, GRPC_SERVER_ERROR_STATUSES } = require('../src/constants')
const path = require('path')

describe('Config', () => {
let Config
Expand Down Expand Up @@ -2342,4 +2343,84 @@ describe('Config', () => {
expect(taggingConfig).to.have.property('maxDepth', 7)
})
})

context('library config', () => {
let env
let tempDir
beforeEach(() => {
env = process.env
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'config-test-'))
process.env.DD_TEST_LOCAL_CONFIG_PATH = path.join(tempDir, 'local.yaml')
process.env.DD_TEST_FLEET_CONFIG_PATH = path.join(tempDir, 'fleet.yaml')
})

afterEach(() => {
process.env = env
fs.rmdirSync(tempDir, { recursive: true })
})

it('should apply host wide config', () => {
fs.writeFileSync(
process.env.DD_TEST_LOCAL_CONFIG_PATH,
`
apm_configuration_default:
DD_RUNTIME_METRICS_ENABLED: true
`)
const config = new Config()
expect(config).to.have.property('runtimeMetrics', true)
})

it('should apply service specific config', () => {
fs.writeFileSync(
process.env.DD_TEST_LOCAL_CONFIG_PATH,
`
rules:
- selectors:
- origin: language
matches:
- nodejs
operator: equals
configuration:
DD_SERVICE: my-service
`)
const config = new Config()
expect(config).to.have.property('service', 'my-service')
})

it('should respect the priority orders', () => {
fs.writeFileSync(
process.env.DD_TEST_LOCAL_CONFIG_PATH,
`
rules:
- selectors:
- origin: language
matches:
- nodejs
operator: equals
configuration:
DD_SERVICE: a
`)
const configA = new Config()
expect(configA).to.have.property('service', 'a')

process.env.DD_SERVICE = 'b'
const configB = new Config()
expect(configB).to.have.property('service', 'b', 'local stable config < env var')

fs.writeFileSync(
process.env.DD_TEST_FLEET_CONFIG_PATH,
`
rules:
- selectors:
- origin: language
matches:
- nodejs
operator: equals
configuration:
DD_SERVICE: c
`)
const configC = new Config()
expect(configC).to.have.property('service', 'c', 'local stable config < fleet config < env var')
})
})
})
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,10 @@
resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz"
integrity "sha1-u1BFecHK6SPmV2pPXaQ9Jfl729k= sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="

"@datadog/libdatadog@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@datadog/libdatadog/-/libdatadog-0.4.0.tgz#aeeea02973f663b555ad9ac30c4015a31d561598"
integrity sha512-kGZfFVmQInzt6J4FFGrqMbrDvOxqwk3WqhAreS6n9b/De+iMVy/NMu3V7uKsY5zAvz+uQw0liDJm3ZDVH/MVVw==
"@datadog/libdatadog@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@datadog/libdatadog/-/libdatadog-0.5.0.tgz#0ef2a2a76bb9505a0e7e5bc9be1415b467dbf368"
integrity sha512-YvLUVOhYVjJssm0f22/RnDQMc7ZZt/w1bA0nty1vvjyaDz5EWaHfWaaV4GYpCt5MRvnGjCBxIwwbRivmGseKeQ==

"@datadog/[email protected]":
version "8.4.0"
Expand Down

0 comments on commit 2bcf68f

Please sign in to comment.