UNPKG

dd-trace

Version:

Datadog APM tracing client for JavaScript

268 lines (238 loc) 8.3 kB
'use strict' /* eslint-disable eslint-rules/eslint-process-env */ /** * @typedef {object} SupportedConfigurationEntry * @property {string} implementation * @property {string} type * @property {string|number|boolean|null|object|unknown[]} default * @property {string[]} [aliases] * @property {string[]} [configurationNames] * @property {string} [internalPropertyName] * @property {string} [transform] * @property {string} [allowed] * @property {string|boolean} [deprecated] */ /** * @typedef {object} SupportedConfigurationsJson * @property {Record<`DD_${string}` | `OTEL_${string}`, SupportedConfigurationEntry[]>} supportedConfigurations */ const { deprecate } = require('util') const { DD_MAJOR } = require('../../../../version') const applyMajorOverrides = require('./major-overrides') const { supportedConfigurations, } = /** @type {SupportedConfigurationsJson} */ (require('./supported-configurations.json')) applyMajorOverrides(supportedConfigurations, DD_MAJOR) /** * Types for environment variable handling. * * @typedef {keyof typeof supportedConfigurations} SupportedEnvKey * @typedef {Partial<typeof process.env> & Partial<Record<SupportedEnvKey, string|undefined>>} TracerEnv */ // Backwards-compatible views for old helper logic: // - `aliases`: Record<canonicalEnvVar, string[]> // - `deprecations`: Record<deprecatedEnvVar, string> (message suffix) const aliases = {} const deprecations = {} for (const [canonical, configuration] of Object.entries(supportedConfigurations)) { for (const implementation of configuration) { if (implementation.deprecated) { deprecations[canonical] = implementation.deprecated // Deprecated entries with an alias may not be listed in the supported configurations map if (implementation.aliases) { delete supportedConfigurations[canonical] continue } } if (Array.isArray(implementation.aliases)) { for (const alias of implementation.aliases) { aliases[canonical] ??= new Set() aliases[canonical].add(alias) } } } } const aliasToCanonical = {} for (const canonical of Object.keys(aliases)) { for (const alias of aliases[canonical]) { if (supportedConfigurations[alias]) { // Allow 'fallback' aliases to be used for other configurations. // This is used to handle the case where an alias could be used for multiple configurations. // For example, OTEL_EXPORTER_OTLP_ENDPOINT is used for OTEL_EXPORTER_OTLP_LOGS_ENDPOINT // and OTEL_EXPORTER_OTLP_METRICS_ENDPOINT. continue } if (aliasToCanonical[alias]) { throw new Error(`The alias ${alias} is already used for ${aliasToCanonical[alias]}.`) } aliasToCanonical[alias] = canonical } } const deprecationMethods = {} for (const deprecation of Object.keys(deprecations)) { deprecationMethods[deprecation] = deprecate( () => {}, `The environment variable ${deprecation} is deprecated.` + (aliasToCanonical[deprecation] ? ` Please use ${aliasToCanonical[deprecation]} instead.` : ` ${deprecations[deprecation]}`), `DATADOG_${deprecation}` ) } let localStableConfig let fleetStableConfig let stableConfigWarnings let stableConfigLoaded = false function loadStableConfig () { stableConfigLoaded = true // Lazy require to avoid circular dependency at module load time. const { IS_SERVERLESS } = require('../serverless') if (IS_SERVERLESS) { // Stable config is not supported in serverless environments. return } const StableConfig = require('./stable') const instance = new StableConfig() localStableConfig = instance.localEntries fleetStableConfig = instance.fleetEntries stableConfigWarnings = instance.warnings } function getValueFromSource (name, source) { if (source[name] !== undefined) { return source[name] } if (aliases[name]) { for (const alias of aliases[name]) { if (source[alias] !== undefined) { return source[alias] } } } } function getEnvNameFromSource (name, source) { if (source[name] !== undefined) { return name } if (aliases[name]) { for (const alias of aliases[name]) { if (source[alias] !== undefined) { return alias } } } } function validateAccess (name) { if ((name.startsWith('DD_') || name.startsWith('OTEL_')) && !supportedConfigurations[name] && !aliasToCanonical[name]) { throw new Error(`Missing ${name} env/configuration in "supported-configurations.json" file.`) } } module.exports = { /** * Expose raw stable config maps and warnings for consumers that need * per-source access (e.g. telemetry in Config). * * @returns {{ localStableConfig: object, fleetStableConfig: object, stableConfigWarnings: string[] }} */ getStableConfigSources () { if (!stableConfigLoaded) { loadStableConfig() } return { localStableConfig, fleetStableConfig, stableConfigWarnings, } }, /** * Returns the environment variables that are supported by the tracer * (including all non-Datadog/OTEL specific environment variables). * * This should only be called once in config.js to avoid copying the object frequently. * * @returns {TracerEnv} The environment variables */ getEnvironmentVariables (source = process.env, internalOnly = false) { const configs = {} for (const [key, value] of Object.entries(source)) { if (key.startsWith('DD_') || key.startsWith('OTEL_') || aliasToCanonical[key]) { if (supportedConfigurations[key]) { configs[key] = value } else if (aliasToCanonical[key] && configs[aliasToCanonical[key]] === undefined) { // The alias should only be used if the actual configuration is not set // In case that more than a single alias exist, use the one defined first in our own order for (const alias of aliases[aliasToCanonical[key]]) { if (source[alias] !== undefined) { configs[aliasToCanonical[key]] = value break } } // TODO(BridgeAR) Implement logging. It would have to use a timeout to // lazily log the message after all loading being done otherwise. // debug( // `Missing configuration ${env} in supported-configurations file. The environment variable is ignored.` // ) // This could be moved inside the main config logic. } deprecationMethods[key]?.() } else if (!internalOnly) { configs[key] = value } } return configs }, getEnvironmentVariable (name) { validateAccess(name) return getValueFromSource(name, process.env) }, /** * Returns the value stored at the given name, assumed to be in environment variable format, * from the supported env sources (process.env, local stable config, fleet stable config). * Falls back to aliases if the canonical name is not set. * * @param {string} name Environment variable name * @returns {string|undefined} * @throws {Error} if the configuration is not supported */ getValueFromEnvSources (name) { validateAccess(name) if (!stableConfigLoaded) { loadStableConfig() } if (fleetStableConfig !== undefined) { const fromFleet = getValueFromSource(name, fleetStableConfig) if (fromFleet !== undefined) { return fromFleet } } const fromEnv = getValueFromSource(name, process.env) if (fromEnv !== undefined) { return fromEnv } if (localStableConfig !== undefined) { return getValueFromSource(name, localStableConfig) } }, /** * Returns the actual environment variable name used for a supported configuration * from a specific environment-based source. * * @param {string} name Environment variable name * @returns {string|undefined} */ getConfiguredEnvName (name) { validateAccess(name) if (!stableConfigLoaded) { loadStableConfig() } for (const source of [fleetStableConfig, process.env, localStableConfig]) { if (source !== undefined) { const fromSource = getEnvNameFromSource(name, source) if (fromSource !== undefined) { return fromSource } } } }, }