Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade(tracer): Support libdatadog's library_config module #5126

Merged
merged 19 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,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.3.0",
Expand Down
111 changes: 95 additions & 16 deletions packages/dd-trace/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const tagger = require('./tagger')
const get = require('../../datadog-core/src/utils/src/get')
const has = require('../../datadog-core/src/utils/src/has')
const set = require('../../datadog-core/src/utils/src/set')
const { isTrue, isFalse } = require('./util')
const { isTrue, isFalse, normalizeProfilingEnabledValue } = require('./util')
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags')
const { getGitMetadataFromGitProperties, removeUserSensitiveInfo } = require('./git_properties')
const { updateConfig } = require('./telemetry')
Expand Down Expand Up @@ -236,6 +236,12 @@ function reformatSpanSamplingRules (rules) {

class Config {
constructor (options = {}) {
if (!isInServerlessEnvironment()) {
// Bail out early if we're in a serverless environment, stable config isn't supported
const StableConfig = require('./config_stable')
this.stableConfig = new StableConfig()
}

options = {
...options,
appsec: options.appsec != null ? options.appsec : options.experimental?.appsec,
Expand All @@ -244,13 +250,24 @@ 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 = log.isEnabled(
this.stableConfig?.fleetEntries?.DD_TRACE_DEBUG,
this.stableConfig?.localEntries?.DD_TRACE_DEBUG
)
this.logger = coalesce(options.logger, logConfig.logger)
this.logLevel = coalesce(options.logLevel, logConfig.logLevel)

this.logLevel = log.getLogLevel(
options.logLevel,
this.stableConfig?.fleetEntries?.DD_TRACE_LOG_LEVEL,
this.stableConfig?.localEntries?.DD_TRACE_LOG_LEVEL
)
log.use(this.logger)
log.toggle(this.debug, this.logLevel)

// Process stable config warnings, if any
for (const warning of this.stableConfig?.warnings ?? []) {
log.warn(warning)
}

checkIfBothOtelAndDdEnvVarSet()

const DD_API_KEY = coalesce(
Expand Down Expand Up @@ -337,7 +354,9 @@ class Config {
}

this._applyDefaults()
this._applyLocalStableConfig()
this._applyEnvironment()
this._applyFleetStableConfig()
this._applyOptions(options)
this._applyCalculated()
this._applyRemote({})
Expand Down Expand Up @@ -576,6 +595,45 @@ class Config {
this._setValue(defaults, 'trace.dynamoDb.tablePrimaryKeys', undefined)
}

_applyLocalStableConfig () {
const obj = setHiddenProperty(this, '_localStableConfig', {})
this._applyStableConfig(this.stableConfig?.localEntries ?? {}, obj)
}

_applyFleetStableConfig () {
const obj = setHiddenProperty(this, '_fleetStableConfig', {})
this._applyStableConfig(this.stableConfig?.fleetEntries ?? {}, 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 profilingEnabled = normalizeProfilingEnabledValue(DD_PROFILING_ENABLED)
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 @@ -831,16 +889,13 @@ class Config {
this._envUnprocessed.peerServiceMapping = DD_TRACE_PEER_SERVICE_MAPPING
}
this._setString(env, 'port', DD_TRACE_AGENT_PORT)
const profilingEnabledEnv = coalesce(
DD_EXPERIMENTAL_PROFILING_ENABLED,
DD_PROFILING_ENABLED,
this._isInServerlessEnvironment() ? 'false' : undefined
const profilingEnabled = normalizeProfilingEnabledValue(
coalesce(
DD_EXPERIMENTAL_PROFILING_ENABLED,
DD_PROFILING_ENABLED,
this._isInServerlessEnvironment() ? 'false' : undefined
)
)
const profilingEnabled = isTrue(profilingEnabledEnv)
? 'true'
: isFalse(profilingEnabledEnv)
? 'false'
: profilingEnabledEnv === 'auto' ? 'auto' : undefined
this._setString(env, 'profiling.enabled', profilingEnabled)
this._setString(env, 'profiling.exporters', DD_PROFILING_EXPORTERS)
this._setBoolean(env, 'profiling.sourceMap', DD_PROFILING_SOURCE_MAP && !isFalse(DD_PROFILING_SOURCE_MAP))
Expand Down Expand Up @@ -1347,9 +1402,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
100 changes: 100 additions & 0 deletions packages/dd-trace/src/config_stable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const os = require('os')
const fs = require('fs')

class StableConfig {
constructor () {
this.warnings = [] // Logger hasn't been initialized yet, so we can't use log.warn
this.localEntries = {}
this.fleetEntries = {}
this.wasm_loaded = false

const { localConfigPath, fleetConfigPath } = this._getStableConfigPaths()
if (!fs.existsSync(localConfigPath) && !fs.existsSync(fleetConfigPath)) {
// Bail out early if files don't exist to avoid unnecessary library loading
return
}

const localConfig = this._readConfigFromPath(localConfigPath)
const fleetConfig = this._readConfigFromPath(fleetConfigPath)
if (!localConfig && !fleetConfig) {
// Bail out early if files are empty or we can't read them to avoid unnecessary library loading
return
}

// Note: we don't enforce loading 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.
let libdatadog
try {
libdatadog = require('@datadog/libdatadog')
this.wasm_loaded = true
} catch (e) {
this.warnings.push('Can\'t load libdatadog library')
return
}

const libconfig = libdatadog.maybeLoad('library_config')
if (libconfig === undefined) {
this.warnings.push('Can\'t load library_config library')
return
}

try {
const configurator = new libconfig.JsConfigurator()
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') {
this.localEntries[entry.name] = entry.value
} else if (entry.source === 'fleet_stable_config') {
this.fleetEntries[entry.name] = entry.value
}
})
} catch (e) {
this.warnings.push(`Error parsing configuration from file: ${e.message}`)
}
}

_readConfigFromPath (path) {
try {
return fs.readFileSync(path, 'utf8')
} catch (err) {
if (err.code !== 'ENOENT') {
this.warnings.push(`Error reading config file at ${path}. ${err.code}: ${err.message}`)
}
return '' // Always return a string to avoid undefined.toString() errors
}
}

_getStableConfigPaths () {
let localConfigPath = ''
let fleetConfigPath = ''
switch (os.type().toLowerCase()) {
case 'linux':
localConfigPath = '/etc/datadog-agent/application_monitoring.yaml'
fleetConfigPath = '/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml'
break
case 'darwin':
localConfigPath = '/opt/datadog-agent/etc/application_monitoring.yaml'
fleetConfigPath = '/opt/datadog-agent/etc/managed/datadog-agent/stable/application_monitoring.yaml'
break
case 'win32':
localConfigPath = 'C:\\ProgramData\\Datadog\\application_monitoring.yaml'
fleetConfigPath = 'C:\\ProgramData\\Datadog\\managed\\datadog-agent\\stable\\application_monitoring.yaml'
break
default:
break
}

// Allow overriding the paths for testing
if (process.env.DD_TEST_LOCAL_CONFIG_PATH !== undefined) {
localConfigPath = process.env.DD_TEST_LOCAL_CONFIG_PATH
}
if (process.env.DD_TEST_FLEET_CONFIG_PATH !== undefined) {
fleetConfigPath = process.env.DD_TEST_FLEET_CONFIG_PATH
}

return { localConfigPath, fleetConfigPath }
}
}

module.exports = StableConfig
39 changes: 26 additions & 13 deletions packages/dd-trace/src/log/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,36 @@ const log = {

deprecate (code, message) {
return this._deprecate(code, message)
},

isEnabled (fleetStableConfigValue = undefined, localStableConfigValue = undefined) {
return isTrue(coalesce(
fleetStableConfigValue,
process.env?.DD_TRACE_DEBUG,
process.env?.OTEL_LOG_LEVEL === 'debug' || undefined,
localStableConfigValue,
config.enabled
))
},

getLogLevel (
optionsValue = undefined,
fleetStableConfigValue = undefined,
localStableConfigValue = undefined
) {
return coalesce(
optionsValue,
fleetStableConfigValue,
process.env?.DD_TRACE_LOG_LEVEL,
process.env?.OTEL_LOG_LEVEL,
localStableConfigValue,
config.logLevel
)
}
}

log.reset()

const enabled = isTrue(coalesce(
process.env.DD_TRACE_DEBUG,
process.env.OTEL_LOG_LEVEL === 'debug',
config.enabled
))

const logLevel = coalesce(
process.env.DD_TRACE_LOG_LEVEL,
process.env.OTEL_LOG_LEVEL,
config.logLevel
)

log.toggle(enabled, logLevel)
log.toggle(log.isEnabled(), log.getLogLevel())

module.exports = log
11 changes: 10 additions & 1 deletion packages/dd-trace/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,20 @@ function hasOwn (object, prop) {
return Object.prototype.hasOwnProperty.call(object, prop)
}

function normalizeProfilingEnabledValue (configValue) {
return isTrue(configValue)
? 'true'
: isFalse(configValue)
? 'false'
: configValue === 'auto' ? 'auto' : undefined
}

module.exports = {
isTrue,
isFalse,
isError,
globMatch,
calculateDDBasePath,
hasOwn
hasOwn,
normalizeProfilingEnabledValue
}
Loading
Loading