UNPKG

dd-trace

Version:

Datadog APM tracing client for JavaScript

1,075 lines (1,006 loc) 70.2 kB
'use strict' const fs = require('fs') const os = require('os') const uuid = require('../../../vendor/dist/crypto-randomuuid') // we need to keep the old uuid dep because of cypress const { URL } = require('url') const log = require('./log') const tagger = require('./tagger') const set = require('../../datadog-core/src/utils/src/set') const { isTrue, isFalse, normalizeProfilingEnabledValue } = require('./util') const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('./plugins/util/tags') const { getGitMetadataFromGitProperties, removeUserSensitiveInfo, getRemoteOriginURL, resolveGitHeadSHA } = require('./git_properties') const { updateConfig } = require('./telemetry') const telemetryMetrics = require('./telemetry/metrics') const { isInServerlessEnvironment, getIsGCPFunction, getIsAzureFunction } = require('./serverless') const { ORIGIN_KEY } = require('./constants') const { appendRules } = require('./payload-tagging/config') const { getEnvironmentVariable: getEnv, getEnvironmentVariables } = require('./config-helper') const defaults = require('./config_defaults') const path = require('path') const { DD_MAJOR } = require('../../../version') const tracerMetrics = telemetryMetrics.manager.namespace('tracers') const changeTracker = {} const telemetryCounters = { 'otel.env.hiding': {}, 'otel.env.invalid': {} } function getCounter (event, ddVar, otelVar) { const counters = telemetryCounters[event] const tags = [] const ddVarPrefix = 'config_datadog:' const otelVarPrefix = 'config_opentelemetry:' if (ddVar) { ddVar = ddVarPrefix + ddVar.toLowerCase() tags.push(ddVar) } if (otelVar) { otelVar = otelVarPrefix + otelVar.toLowerCase() tags.push(otelVar) } if (!(otelVar in counters)) counters[otelVar] = {} const counter = tracerMetrics.count(event, tags) counters[otelVar][ddVar] = counter return counter } const otelDdEnvMapping = { OTEL_LOG_LEVEL: 'DD_TRACE_LOG_LEVEL', OTEL_PROPAGATORS: 'DD_TRACE_PROPAGATION_STYLE', OTEL_SERVICE_NAME: 'DD_SERVICE', OTEL_TRACES_SAMPLER: 'DD_TRACE_SAMPLE_RATE', OTEL_TRACES_SAMPLER_ARG: 'DD_TRACE_SAMPLE_RATE', OTEL_TRACES_EXPORTER: 'DD_TRACE_ENABLED', OTEL_METRICS_EXPORTER: 'DD_RUNTIME_METRICS_ENABLED', OTEL_RESOURCE_ATTRIBUTES: 'DD_TAGS', OTEL_SDK_DISABLED: 'DD_TRACE_OTEL_ENABLED', OTEL_LOGS_EXPORTER: undefined } const VALID_PROPAGATION_STYLES = new Set(['datadog', 'tracecontext', 'b3', 'b3 single header', 'none']) const VALID_PROPAGATION_BEHAVIOR_EXTRACT = new Set(['continue', 'restart', 'ignore']) const VALID_LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error']) const DEFAULT_OTLP_PORT = 4318 function getFromOtelSamplerMap (otelTracesSampler, otelTracesSamplerArg) { const OTEL_TRACES_SAMPLER_MAPPING = { always_on: '1.0', always_off: '0.0', traceidratio: otelTracesSamplerArg, parentbased_always_on: '1.0', parentbased_always_off: '0.0', parentbased_traceidratio: otelTracesSamplerArg } return OTEL_TRACES_SAMPLER_MAPPING[otelTracesSampler] } /** * Validate the type of an environment variable * @param {string} envVar - The name of the environment variable * @param {string} [value] - The value of the environment variable * @returns {boolean} - True if the value is valid, false otherwise */ function isInvalidOtelEnvironmentVariable (envVar, value) { // Skip validation if the value is undefined (it was not set as environment variable) if (value === undefined) return false switch (envVar) { case 'OTEL_LOG_LEVEL': return !VALID_LOG_LEVELS.has(value) case 'OTEL_PROPAGATORS': case 'OTEL_RESOURCE_ATTRIBUTES': case 'OTEL_SERVICE_NAME': return typeof value !== 'string' case 'OTEL_TRACES_SAMPLER': return getFromOtelSamplerMap(value, getEnv('OTEL_TRACES_SAMPLER_ARG')) === undefined case 'OTEL_TRACES_SAMPLER_ARG': return Number.isNaN(Number.parseFloat(value)) case 'OTEL_SDK_DISABLED': return value.toLowerCase() !== 'true' && value.toLowerCase() !== 'false' case 'OTEL_TRACES_EXPORTER': case 'OTEL_METRICS_EXPORTER': case 'OTEL_LOGS_EXPORTER': return value.toLowerCase() !== 'none' default: return true } } function checkIfBothOtelAndDdEnvVarSet () { for (const [otelEnvVar, ddEnvVar] of Object.entries(otelDdEnvMapping)) { const otelValue = getEnv(otelEnvVar) if (ddEnvVar && getEnv(ddEnvVar) && otelValue) { log.warn('both %s and %s environment variables are set', ddEnvVar, otelEnvVar) getCounter('otel.env.hiding', ddEnvVar, otelEnvVar).inc() } if (isInvalidOtelEnvironmentVariable(otelEnvVar, otelValue)) { log.warn('unexpected value %s for %s environment variable', otelValue, otelEnvVar) getCounter('otel.env.invalid', ddEnvVar, otelEnvVar).inc() } } } const runtimeId = uuid() function maybeFile (filepath) { if (!filepath) return try { return fs.readFileSync(filepath, 'utf8') } catch (e) { log.error('Error reading file %s', filepath, e) } } function maybeJsonFile (filepath) { const file = maybeFile(filepath) if (!file) return try { return JSON.parse(file) } catch (e) { log.error('Error parsing JSON file %s', filepath, e) } } function safeJsonParse (input) { try { return JSON.parse(input) } catch {} } const namingVersions = new Set(['v0', 'v1']) const defaultNamingVersion = 'v0' function validateNamingVersion (versionString) { if (!versionString) { return defaultNamingVersion } if (!namingVersions.has(versionString)) { log.warn('Unexpected input for config.spanAttributeSchema, picked default', defaultNamingVersion) return defaultNamingVersion } return versionString } /** * Given a string of comma-separated paths, return the array of paths. * If a blank path is provided a null is returned to signal that the feature is disabled. * An empty array means the feature is enabled but that no rules need to be applied. * * @param {string | string[]} input */ function splitJSONPathRules (input) { if (!input) return if (Array.isArray(input)) return input if (input === 'all') return [] return input.split(',') } // Shallow clone with property name remapping function remapify (input, mappings) { if (!input) return const output = {} for (const [key, value] of Object.entries(input)) { output[key in mappings ? mappings[key] : key] = value } return output } /** * Normalizes propagation style values to a lowercase array. * Handles both string (comma-separated) and array inputs. */ function normalizePropagationStyle (value) { if (Array.isArray(value)) { return value.map(v => v.toLowerCase()) } if (typeof value === 'string') { return value.split(',') .filter(v => v !== '') .map(v => v.trim().toLowerCase()) } if (value !== undefined) { log.warn('Unexpected input for config.tracePropagationStyle') } } /** * Warns if both DD_TRACE_PROPAGATION_STYLE and specific inject/extract vars are set. */ function warnIfPropagationStyleConflict (general, inject, extract) { if (general && (inject || extract)) { log.warn( // eslint-disable-next-line @stylistic/max-len 'Use either the DD_TRACE_PROPAGATION_STYLE environment variable or separate DD_TRACE_PROPAGATION_STYLE_INJECT and DD_TRACE_PROPAGATION_STYLE_EXTRACT environment variables' ) } } function reformatSpanSamplingRules (rules) { if (!rules) return rules return rules.map(rule => { return remapify(rule, { sample_rate: 'sampleRate', max_per_second: 'maxPerSecond' }) }) } class Config { /** * parsed DD_TAGS, usable as a standalone tag set across products * @type {Record<string, string> | undefined} */ #parsedDdTags = {} #envUnprocessed = {} #optsUnprocessed = {} #remoteUnprocessed = {} #env = {} #options = {} #remote = {} #defaults = {} #optionsArg = {} #localStableConfig = {} #fleetStableConfig = {} #calculated = {} #getSourcesInOrder () { return [ { container: this.#remote, origin: 'remote_config', unprocessed: this.#remoteUnprocessed }, { container: this.#options, origin: 'code', unprocessed: this.#optsUnprocessed }, { container: this.#fleetStableConfig, origin: 'fleet_stable_config' }, { container: this.#env, origin: 'env_var', unprocessed: this.#envUnprocessed }, { container: this.#localStableConfig, origin: 'local_stable_config' }, { container: this.#calculated, origin: 'calculated' }, { container: this.#defaults, origin: 'default' } ] } 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() } const envs = getEnvironmentVariables() options = { ...options, appsec: options.appsec == null ? options.experimental?.appsec : options.appsec, iast: options.iast == null ? options.experimental?.iast : options.iast } // Configure the logger first so it can be used to warn about other configs const logConfig = log.getConfig() this.debug = log.isEnabled( this.stableConfig?.fleetEntries?.DD_TRACE_DEBUG, this.stableConfig?.localEntries?.DD_TRACE_DEBUG ) this.logger = options.logger ?? logConfig.logger 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() if (typeof options.appsec === 'boolean') { options.appsec = { enabled: options.appsec } } if (typeof options.runtimeMetrics === 'boolean') { options.runtimeMetrics = { enabled: options.runtimeMetrics } } this.#defaults = defaults this.#applyDefaults() this.#applyStableConfig(this.stableConfig?.localEntries ?? {}, this.#localStableConfig) this.#applyEnvironment(envs) this.#applyStableConfig(this.stableConfig?.fleetEntries ?? {}, this.#fleetStableConfig) this.#applyOptions(options) this.#applyCalculated() this.#applyRemote({}) this.#merge() tagger.add(this.tags, { service: this.service, env: this.env, version: this.version, 'runtime-id': runtimeId }) if (this.isCiVisibility) { tagger.add(this.tags, { [ORIGIN_KEY]: 'ciapp-test' }) } if (this.gitMetadataEnabled) { this.#loadGitMetadata(envs) } } get parsedDdTags () { return this.#parsedDdTags } // Supports only a subset of options for now. configure (options, remote) { if (remote) { this.#applyRemote(options) } else { this.#applyOptions(options) } // TODO: test this.#applyCalculated() this.#merge() } #getDefaultPropagationStyle (options) { // TODO: Remove the experimental env vars as a major? const DD_TRACE_B3_ENABLED = options.experimental?.b3 ?? getEnv('DD_TRACE_EXPERIMENTAL_B3_ENABLED') const defaultPropagationStyle = ['datadog', 'tracecontext'] if (isTrue(DD_TRACE_B3_ENABLED)) { defaultPropagationStyle.push('b3', 'b3 single header') } return defaultPropagationStyle } _isInServerlessEnvironment () { return isInServerlessEnvironment() } #applyStableConfig (config, obj) { this.#applyConfigValues(config, obj, {}) } // Set environment-dependent defaults that can be overridden by users #applyDefaults () { const defaults = this.#defaults if (isInServerlessEnvironment()) { this.#setBoolean(defaults, 'crashtracking.enabled', false) this.#setString(defaults, 'profiling.enabled', 'false') this.#setBoolean(defaults, 'telemetry.enabled', false) this.#setBoolean(defaults, 'remoteConfig.enabled', false) } else { this.#setBoolean(defaults, 'crashtracking.enabled', true) } if (getEnv('JEST_WORKER_ID')) { this.#setBoolean(defaults, 'telemetry.enabled', false) } } #applyEnvironment () { this.#applyConfigValues(getEnvironmentVariables(), this.#env, this.#envUnprocessed) } #applyConfigValues (source, target, unprocessedTarget) { const { AWS_LAMBDA_FUNCTION_NAME, DD_AGENT_HOST, DD_AI_GUARD_ENABLED, DD_AI_GUARD_ENDPOINT, DD_AI_GUARD_MAX_CONTENT_SIZE, DD_AI_GUARD_MAX_MESSAGES_LENGTH, DD_AI_GUARD_TIMEOUT, DD_API_KEY, DD_API_SECURITY_ENABLED, DD_API_SECURITY_SAMPLE_DELAY, DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED, DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT, DD_APM_TRACING_ENABLED, DD_APP_KEY, DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE, DD_APPSEC_COLLECT_ALL_HEADERS, DD_APPSEC_ENABLED, DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON, DD_APPSEC_HEADER_COLLECTION_REDACTION_ENABLED, DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML, DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON, DD_APPSEC_MAX_COLLECTED_HEADERS, DD_APPSEC_MAX_STACK_TRACES, DD_APPSEC_MAX_STACK_TRACE_DEPTH, DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP, DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP, DD_APPSEC_RULES, DD_APPSEC_SCA_ENABLED, DD_APPSEC_STACK_TRACE_ENABLED, DD_APPSEC_RASP_ENABLED, DD_APPSEC_RASP_COLLECT_REQUEST_BODY, DD_APPSEC_TRACE_RATE_LIMIT, DD_APPSEC_WAF_TIMEOUT, DD_CRASHTRACKING_ENABLED, DD_CODE_ORIGIN_FOR_SPANS_ENABLED, DD_CODE_ORIGIN_FOR_SPANS_EXPERIMENTAL_EXIT_SPANS_ENABLED, DD_DATA_STREAMS_ENABLED, DD_DBM_PROPAGATION_MODE, DD_DOGSTATSD_HOST, DD_DOGSTATSD_PORT, DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS, DD_DYNAMIC_INSTRUMENTATION_ENABLED, DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE, DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS, DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS, DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS, DD_ENV, DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED, DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, DD_PROFILING_ENABLED, DD_GRPC_CLIENT_ERROR_STATUSES, DD_GRPC_SERVER_ERROR_STATUSES, DD_HEAP_SNAPSHOT_COUNT, DD_HEAP_SNAPSHOT_DESTINATION, DD_HEAP_SNAPSHOT_INTERVAL, DD_IAST_DB_ROWS_TO_TAINT, DD_IAST_DEDUPLICATION_ENABLED, DD_IAST_ENABLED, DD_IAST_MAX_CONCURRENT_REQUESTS, DD_IAST_MAX_CONTEXT_OPERATIONS, DD_IAST_REDACTION_ENABLED, DD_IAST_REDACTION_NAME_PATTERN, DD_IAST_REDACTION_VALUE_PATTERN, DD_IAST_REQUEST_SAMPLING, DD_IAST_SECURITY_CONTROLS_CONFIGURATION, DD_IAST_TELEMETRY_VERBOSITY, DD_IAST_STACK_TRACE_ENABLED, DD_INJECTION_ENABLED, DD_INJECT_FORCE, DD_INSTRUMENTATION_TELEMETRY_ENABLED, DD_INSTRUMENTATION_CONFIG_ID, DD_LOGS_INJECTION, DD_LOGS_OTEL_ENABLED, DD_METRICS_OTEL_ENABLED, DD_LANGCHAIN_SPAN_CHAR_LIMIT, DD_LANGCHAIN_SPAN_PROMPT_COMPLETION_SAMPLE_RATE, DD_LLMOBS_AGENTLESS_ENABLED, DD_LLMOBS_ENABLED, DD_LLMOBS_ML_APP, DD_OPENAI_LOGS_ENABLED, DD_OPENAI_SPAN_CHAR_LIMIT, DD_PROFILING_EXPORTERS, DD_PROFILING_SOURCE_MAP, DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD, DD_INSTRUMENTATION_INSTALL_ID, DD_INSTRUMENTATION_INSTALL_TIME, DD_INSTRUMENTATION_INSTALL_TYPE, DD_REMOTE_CONFIGURATION_ENABLED, DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS, DD_RUNTIME_METRICS_ENABLED, DD_RUNTIME_METRICS_EVENT_LOOP_ENABLED, DD_RUNTIME_METRICS_GC_ENABLED, DD_SERVICE, DD_SERVICE_MAPPING, DD_SITE, DD_SPAN_SAMPLING_RULES, DD_SPAN_SAMPLING_RULES_FILE, DD_TAGS, DD_TELEMETRY_DEBUG, DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED, DD_TELEMETRY_HEARTBEAT_INTERVAL, DD_TELEMETRY_LOG_COLLECTION_ENABLED, DD_TELEMETRY_METRICS_ENABLED, DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED, DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED, DD_TRACE_AGENT_PORT, DD_TRACE_AGENT_PROTOCOL_VERSION, DD_TRACE_AWS_ADD_SPAN_POINTERS, DD_TRACE_BAGGAGE_MAX_BYTES, DD_TRACE_BAGGAGE_MAX_ITEMS, DD_TRACE_BAGGAGE_TAG_KEYS, DD_TRACE_CLIENT_IP_ENABLED, DD_TRACE_CLIENT_IP_HEADER, DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING, DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH, DD_TRACE_DYNAMODB_TABLE_PRIMARY_KEYS, DD_TRACE_ENABLED, DD_TRACE_EXPERIMENTAL_EXPORTER, DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED, DD_RUNTIME_METRICS_RUNTIME_ID_ENABLED, DD_TRACE_GIT_METADATA_ENABLED, DD_TRACE_GLOBAL_TAGS, DD_TRACE_GRAPHQL_ERROR_EXTENSIONS, DD_TRACE_HEADER_TAGS, DD_TRACE_LEGACY_BAGGAGE_ENABLED, DD_TRACE_MEMCACHED_COMMAND_ENABLED, DD_TRACE_MIDDLEWARE_TRACING_ENABLED, DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP, DD_TRACE_PARTIAL_FLUSH_MIN_SPANS, DD_TRACE_FLUSH_INTERVAL, DD_TRACE_PEER_SERVICE_MAPPING, DD_TRACE_PROPAGATION_EXTRACT_FIRST, DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT, DD_TRACE_PROPAGATION_STYLE, DD_TRACE_PROPAGATION_STYLE_INJECT, DD_TRACE_PROPAGATION_STYLE_EXTRACT, DD_TRACE_RATE_LIMIT, DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED, DD_TRACE_REPORT_HOSTNAME, DD_TRACE_RESOURCE_RENAMING_ENABLED, DD_TRACE_SAMPLE_RATE, DD_TRACE_SAMPLING_RULES, DD_TRACE_SCOPE, DD_TRACE_SPAN_ATTRIBUTE_SCHEMA, DD_TRACE_SPAN_LEAK_DEBUG, DD_TRACE_STARTUP_LOGS, DD_TRACE_TAGS, DD_TRACE_WEBSOCKET_MESSAGES_ENABLED, DD_TRACE_WEBSOCKET_MESSAGES_INHERIT_SAMPLING, DD_TRACE_WEBSOCKET_MESSAGES_SEPARATE_TRACES, DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH, DD_TRACING_ENABLED, DD_VERSION, DD_VERTEXAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE, DD_VERTEXAI_SPAN_CHAR_LIMIT, DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED, DD_TRACE_NATIVE_SPAN_EVENTS, OTEL_METRICS_EXPORTER, OTEL_PROPAGATORS, OTEL_RESOURCE_ATTRIBUTES, OTEL_SERVICE_NAME, OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG, DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED, OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_HEADERS, OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_HEADERS, OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT, OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, OTEL_METRIC_EXPORT_TIMEOUT, OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_BSP_SCHEDULE_DELAY, OTEL_BSP_MAX_EXPORT_BATCH_SIZE, OTEL_BSP_MAX_QUEUE_SIZE, OTEL_METRIC_EXPORT_INTERVAL } = source const tags = {} const parsedDdTags = parseSpaceSeparatedTags(DD_TAGS) tagger.add(this.#parsedDdTags, parsedDdTags) tagger.add(tags, parseSpaceSeparatedTags(handleOtel(OTEL_RESOURCE_ATTRIBUTES))) tagger.add(tags, parsedDdTags) tagger.add(tags, DD_TRACE_TAGS) tagger.add(tags, DD_TRACE_GLOBAL_TAGS) this.#setString(target, 'apiKey', DD_API_KEY) this.#setBoolean(target, 'otelLogsEnabled', DD_LOGS_OTEL_ENABLED) // Set OpenTelemetry logs configuration with specific _LOGS_ vars taking precedence over generic _EXPORTERS_ vars if (OTEL_EXPORTER_OTLP_ENDPOINT) { // Only set if there's a custom URL, otherwise let calc phase handle the default this.#setString(target, 'otelUrl', OTEL_EXPORTER_OTLP_ENDPOINT) } if (OTEL_EXPORTER_OTLP_ENDPOINT || OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) { this.#setString(target, 'otelLogsUrl', OTEL_EXPORTER_OTLP_LOGS_ENDPOINT || target.otelUrl) } this.#setString(target, 'otelHeaders', OTEL_EXPORTER_OTLP_HEADERS) this.#setString(target, 'otelLogsHeaders', OTEL_EXPORTER_OTLP_LOGS_HEADERS || target.otelHeaders) this.#setString(target, 'otelProtocol', OTEL_EXPORTER_OTLP_PROTOCOL) this.#setString(target, 'otelLogsProtocol', OTEL_EXPORTER_OTLP_LOGS_PROTOCOL || target.otelProtocol) const otelTimeout = nonNegInt(OTEL_EXPORTER_OTLP_TIMEOUT, 'OTEL_EXPORTER_OTLP_TIMEOUT') if (otelTimeout !== undefined) { target.otelTimeout = otelTimeout } const otelLogsTimeout = nonNegInt(OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, 'OTEL_EXPORTER_OTLP_LOGS_TIMEOUT') target.otelLogsTimeout = otelLogsTimeout === undefined ? target.otelTimeout : otelLogsTimeout const otelBatchTimeout = nonNegInt(OTEL_BSP_SCHEDULE_DELAY, 'OTEL_BSP_SCHEDULE_DELAY', false) if (otelBatchTimeout !== undefined) { target.otelBatchTimeout = otelBatchTimeout } target.otelMaxExportBatchSize = nonNegInt(OTEL_BSP_MAX_EXPORT_BATCH_SIZE, 'OTEL_BSP_MAX_EXPORT_BATCH_SIZE', false) target.otelMaxQueueSize = nonNegInt(OTEL_BSP_MAX_QUEUE_SIZE, 'OTEL_BSP_MAX_QUEUE_SIZE', false) const otelMetricsExporterEnabled = OTEL_METRICS_EXPORTER?.toLowerCase() !== 'none' this.#setBoolean( target, 'otelMetricsEnabled', DD_METRICS_OTEL_ENABLED && isTrue(DD_METRICS_OTEL_ENABLED) && otelMetricsExporterEnabled ) // Set OpenTelemetry metrics configuration with specific _METRICS_ vars // taking precedence over generic _EXPORTERS_ vars if (OTEL_EXPORTER_OTLP_ENDPOINT || OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) { this.#setString(target, 'otelMetricsUrl', OTEL_EXPORTER_OTLP_METRICS_ENDPOINT || target.otelUrl) } this.#setString(target, 'otelMetricsHeaders', OTEL_EXPORTER_OTLP_METRICS_HEADERS || target.otelHeaders) this.#setString(target, 'otelMetricsProtocol', OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || target.otelProtocol) const otelMetricsTimeout = nonNegInt(OTEL_EXPORTER_OTLP_METRICS_TIMEOUT, 'OTEL_EXPORTER_OTLP_METRICS_TIMEOUT') target.otelMetricsTimeout = otelMetricsTimeout === undefined ? target.otelTimeout : otelMetricsTimeout target.otelMetricsExportTimeout = nonNegInt(OTEL_METRIC_EXPORT_TIMEOUT, 'OTEL_METRIC_EXPORT_TIMEOUT') target.otelMetricsExportInterval = nonNegInt(OTEL_METRIC_EXPORT_INTERVAL, 'OTEL_METRIC_EXPORT_INTERVAL', false) // Parse temporality preference (default to DELTA for Datadog) if (OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE) { const temporalityPref = OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE.toUpperCase() if (['DELTA', 'CUMULATIVE', 'LOWMEMORY'].includes(temporalityPref)) { this.#setString(target, 'otelMetricsTemporalityPreference', temporalityPref) } } this.#setBoolean( target, 'apmTracingEnabled', DD_APM_TRACING_ENABLED ?? (DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED && isFalse(DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED)) ) this.#setBoolean(target, 'propagateProcessTags.enabled', DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED) this.#setString(target, 'appKey', DD_APP_KEY) this.#setBoolean(target, 'appsec.apiSecurity.enabled', DD_API_SECURITY_ENABLED && isTrue(DD_API_SECURITY_ENABLED)) target['appsec.apiSecurity.sampleDelay'] = maybeFloat(DD_API_SECURITY_SAMPLE_DELAY) this.#setBoolean(target, 'appsec.apiSecurity.endpointCollectionEnabled', DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED) target['appsec.apiSecurity.endpointCollectionMessageLimit'] = maybeInt(DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT) target['appsec.blockedTemplateGraphql'] = maybeFile(DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON) target['appsec.blockedTemplateHtml'] = maybeFile(DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML) unprocessedTarget['appsec.blockedTemplateHtml'] = DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML target['appsec.blockedTemplateJson'] = maybeFile(DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON) unprocessedTarget['appsec.blockedTemplateJson'] = DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON this.#setBoolean(target, 'appsec.enabled', DD_APPSEC_ENABLED) this.#setString(target, 'appsec.eventTracking.mode', DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE) // TODO appsec.extendedHeadersCollection are deprecated, to delete in a major this.#setBoolean(target, 'appsec.extendedHeadersCollection.enabled', DD_APPSEC_COLLECT_ALL_HEADERS) this.#setBoolean( target, 'appsec.extendedHeadersCollection.redaction', DD_APPSEC_HEADER_COLLECTION_REDACTION_ENABLED ) target['appsec.extendedHeadersCollection.maxHeaders'] = maybeInt(DD_APPSEC_MAX_COLLECTED_HEADERS) unprocessedTarget['appsec.extendedHeadersCollection.maxHeaders'] = DD_APPSEC_MAX_COLLECTED_HEADERS this.#setString(target, 'appsec.obfuscatorKeyRegex', DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP) this.#setString(target, 'appsec.obfuscatorValueRegex', DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP) this.#setBoolean(target, 'appsec.rasp.enabled', DD_APPSEC_RASP_ENABLED) // TODO Deprecated, to delete in a major this.#setBoolean(target, 'appsec.rasp.bodyCollection', DD_APPSEC_RASP_COLLECT_REQUEST_BODY) target['appsec.rateLimit'] = maybeInt(DD_APPSEC_TRACE_RATE_LIMIT) unprocessedTarget['appsec.rateLimit'] = DD_APPSEC_TRACE_RATE_LIMIT this.#setString(target, 'appsec.rules', DD_APPSEC_RULES) // DD_APPSEC_SCA_ENABLED is never used locally, but only sent to the backend this.#setBoolean(target, 'appsec.sca.enabled', DD_APPSEC_SCA_ENABLED) this.#setBoolean(target, 'appsec.stackTrace.enabled', DD_APPSEC_STACK_TRACE_ENABLED) target['appsec.stackTrace.maxDepth'] = maybeInt(DD_APPSEC_MAX_STACK_TRACE_DEPTH) unprocessedTarget['appsec.stackTrace.maxDepth'] = DD_APPSEC_MAX_STACK_TRACE_DEPTH target['appsec.stackTrace.maxStackTraces'] = maybeInt(DD_APPSEC_MAX_STACK_TRACES) unprocessedTarget['appsec.stackTrace.maxStackTraces'] = DD_APPSEC_MAX_STACK_TRACES target['appsec.wafTimeout'] = maybeInt(DD_APPSEC_WAF_TIMEOUT) unprocessedTarget['appsec.wafTimeout'] = DD_APPSEC_WAF_TIMEOUT target.baggageMaxBytes = DD_TRACE_BAGGAGE_MAX_BYTES target.baggageMaxItems = DD_TRACE_BAGGAGE_MAX_ITEMS target.baggageTagKeys = DD_TRACE_BAGGAGE_TAG_KEYS this.#setBoolean(target, 'clientIpEnabled', DD_TRACE_CLIENT_IP_ENABLED) this.#setString(target, 'clientIpHeader', DD_TRACE_CLIENT_IP_HEADER?.toLowerCase()) if (DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING || DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING) { if (DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING) { this.#setBoolean(target, 'cloudPayloadTagging.requestsEnabled', true) } if (DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING) { this.#setBoolean(target, 'cloudPayloadTagging.responsesEnabled', true) } target['cloudPayloadTagging.rules'] = appendRules( splitJSONPathRules(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING), splitJSONPathRules(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING) ) } if (DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH) { target['cloudPayloadTagging.maxDepth'] = maybeInt(DD_TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH) } this.#setBoolean(target, 'crashtracking.enabled', DD_CRASHTRACKING_ENABLED) this.#setBoolean(target, 'codeOriginForSpans.enabled', DD_CODE_ORIGIN_FOR_SPANS_ENABLED) this.#setBoolean( target, 'codeOriginForSpans.experimental.exit_spans.enabled', DD_CODE_ORIGIN_FOR_SPANS_EXPERIMENTAL_EXIT_SPANS_ENABLED ) this.#setString(target, 'dbmPropagationMode', DD_DBM_PROPAGATION_MODE) this.#setString(target, 'dogstatsd.hostname', DD_DOGSTATSD_HOST) this.#setString(target, 'dogstatsd.port', DD_DOGSTATSD_PORT) this.#setBoolean(target, 'dsmEnabled', DD_DATA_STREAMS_ENABLED) target['dynamicInstrumentation.captureTimeoutMs'] = maybeInt(DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS) unprocessedTarget['dynamicInstrumentation.captureTimeoutMs'] = DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS this.#setBoolean(target, 'dynamicInstrumentation.enabled', DD_DYNAMIC_INSTRUMENTATION_ENABLED) this.#setString(target, 'dynamicInstrumentation.probeFile', DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE) this.#setArray(target, 'dynamicInstrumentation.redactedIdentifiers', DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS) this.#setArray( target, 'dynamicInstrumentation.redactionExcludedIdentifiers', DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS ) target['dynamicInstrumentation.uploadIntervalSeconds'] = maybeFloat(DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS) unprocessedTarget['dynamicInstrumentation.uploadInterval'] = DD_DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS this.#setString(target, 'env', DD_ENV || tags.env) this.#setBoolean(target, 'experimental.flaggingProvider.enabled', DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED) this.#setBoolean(target, 'traceEnabled', DD_TRACE_ENABLED) this.#setBoolean(target, 'experimental.aiguard.enabled', DD_AI_GUARD_ENABLED) this.#setString(target, 'experimental.aiguard.endpoint', DD_AI_GUARD_ENDPOINT) target['experimental.aiguard.maxContentSize'] = maybeInt(DD_AI_GUARD_MAX_CONTENT_SIZE) unprocessedTarget['experimental.aiguard.maxContentSize'] = DD_AI_GUARD_MAX_CONTENT_SIZE target['experimental.aiguard.maxMessagesLength'] = maybeInt(DD_AI_GUARD_MAX_MESSAGES_LENGTH) unprocessedTarget['experimental.aiguard.maxMessagesLength'] = DD_AI_GUARD_MAX_MESSAGES_LENGTH target['experimental.aiguard.timeout'] = maybeInt(DD_AI_GUARD_TIMEOUT) unprocessedTarget['experimental.aiguard.timeout'] = DD_AI_GUARD_TIMEOUT this.#setBoolean(target, 'experimental.enableGetRumData', DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED) this.#setString(target, 'experimental.exporter', DD_TRACE_EXPERIMENTAL_EXPORTER) if (AWS_LAMBDA_FUNCTION_NAME) { target.flushInterval = 0 } else if (DD_TRACE_FLUSH_INTERVAL) { target.flushInterval = maybeInt(DD_TRACE_FLUSH_INTERVAL) } target.flushMinSpans = maybeInt(DD_TRACE_PARTIAL_FLUSH_MIN_SPANS) unprocessedTarget.flushMinSpans = DD_TRACE_PARTIAL_FLUSH_MIN_SPANS this.#setBoolean(target, 'gitMetadataEnabled', DD_TRACE_GIT_METADATA_ENABLED) this.#setIntegerRangeSet(target, 'grpc.client.error.statuses', DD_GRPC_CLIENT_ERROR_STATUSES) this.#setIntegerRangeSet(target, 'grpc.server.error.statuses', DD_GRPC_SERVER_ERROR_STATUSES) this.#setArray(target, 'headerTags', DD_TRACE_HEADER_TAGS) target['heapSnapshot.count'] = maybeInt(DD_HEAP_SNAPSHOT_COUNT) this.#setString(target, 'heapSnapshot.destination', DD_HEAP_SNAPSHOT_DESTINATION) target['heapSnapshot.interval'] = maybeInt(DD_HEAP_SNAPSHOT_INTERVAL) this.#setString(target, 'hostname', DD_AGENT_HOST) target['iast.dbRowsToTaint'] = maybeInt(DD_IAST_DB_ROWS_TO_TAINT) this.#setBoolean(target, 'iast.deduplicationEnabled', DD_IAST_DEDUPLICATION_ENABLED) this.#setBoolean(target, 'iast.enabled', DD_IAST_ENABLED) target['iast.maxConcurrentRequests'] = maybeInt(DD_IAST_MAX_CONCURRENT_REQUESTS) unprocessedTarget['iast.maxConcurrentRequests'] = DD_IAST_MAX_CONCURRENT_REQUESTS target['iast.maxContextOperations'] = maybeInt(DD_IAST_MAX_CONTEXT_OPERATIONS) unprocessedTarget['iast.maxContextOperations'] = DD_IAST_MAX_CONTEXT_OPERATIONS this.#setBoolean(target, 'iast.redactionEnabled', DD_IAST_REDACTION_ENABLED && !isFalse(DD_IAST_REDACTION_ENABLED)) this.#setString(target, 'iast.redactionNamePattern', DD_IAST_REDACTION_NAME_PATTERN) this.#setString(target, 'iast.redactionValuePattern', DD_IAST_REDACTION_VALUE_PATTERN) const iastRequestSampling = maybeInt(DD_IAST_REQUEST_SAMPLING) if (iastRequestSampling !== undefined && iastRequestSampling > -1 && iastRequestSampling < 101) { target['iast.requestSampling'] = iastRequestSampling } unprocessedTarget['iast.requestSampling'] = DD_IAST_REQUEST_SAMPLING this.#setString(target, 'iast.securityControlsConfiguration', DD_IAST_SECURITY_CONTROLS_CONFIGURATION) this.#setString(target, 'iast.telemetryVerbosity', DD_IAST_TELEMETRY_VERBOSITY) this.#setBoolean(target, 'iast.stackTrace.enabled', DD_IAST_STACK_TRACE_ENABLED) this.#setString(target, 'installSignature.id', DD_INSTRUMENTATION_INSTALL_ID) this.#setString(target, 'installSignature.time', DD_INSTRUMENTATION_INSTALL_TIME) this.#setString(target, 'installSignature.type', DD_INSTRUMENTATION_INSTALL_TYPE) this.#setArray(target, 'injectionEnabled', DD_INJECTION_ENABLED) if (DD_INJECTION_ENABLED !== undefined) { this.#setString(target, 'instrumentationSource', DD_INJECTION_ENABLED ? 'ssi' : 'manual') } this.#setBoolean(target, 'injectForce', DD_INJECT_FORCE) this.#setBoolean(target, 'isAzureFunction', getIsAzureFunction()) this.#setBoolean(target, 'isGCPFunction', getIsGCPFunction()) target['langchain.spanCharLimit'] = maybeInt(DD_LANGCHAIN_SPAN_CHAR_LIMIT) target['langchain.spanPromptCompletionSampleRate'] = maybeFloat(DD_LANGCHAIN_SPAN_PROMPT_COMPLETION_SAMPLE_RATE) this.#setBoolean(target, 'legacyBaggageEnabled', DD_TRACE_LEGACY_BAGGAGE_ENABLED) this.#setBoolean(target, 'llmobs.agentlessEnabled', DD_LLMOBS_AGENTLESS_ENABLED) this.#setBoolean(target, 'llmobs.enabled', DD_LLMOBS_ENABLED) this.#setString(target, 'llmobs.mlApp', DD_LLMOBS_ML_APP) this.#setBoolean(target, 'logInjection', DD_LOGS_INJECTION) // Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent this.#setBoolean(target, 'memcachedCommandEnabled', DD_TRACE_MEMCACHED_COMMAND_ENABLED) this.#setBoolean(target, 'middlewareTracingEnabled', DD_TRACE_MIDDLEWARE_TRACING_ENABLED) this.#setBoolean(target, 'openAiLogsEnabled', DD_OPENAI_LOGS_ENABLED) target['openai.spanCharLimit'] = maybeInt(DD_OPENAI_SPAN_CHAR_LIMIT) unprocessedTarget.openaiSpanCharLimit = DD_OPENAI_SPAN_CHAR_LIMIT if (DD_TRACE_PEER_SERVICE_MAPPING) { target.peerServiceMapping = Object.fromEntries( DD_TRACE_PEER_SERVICE_MAPPING.split(',').map(x => x.trim().split(':')) ) unprocessedTarget.peerServiceMapping = DD_TRACE_PEER_SERVICE_MAPPING } this.#setString(target, 'port', DD_TRACE_AGENT_PORT) const profilingEnabled = normalizeProfilingEnabledValue(DD_PROFILING_ENABLED) this.#setString(target, 'profiling.enabled', profilingEnabled) this.#setString(target, 'profiling.exporters', DD_PROFILING_EXPORTERS) this.#setBoolean(target, 'profiling.sourceMap', DD_PROFILING_SOURCE_MAP && !isFalse(DD_PROFILING_SOURCE_MAP)) if (DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD) { // This is only used in testing to not have to wait 30s target['profiling.longLivedThreshold'] = Number(DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD) } this.#setString(target, 'protocolVersion', DD_TRACE_AGENT_PROTOCOL_VERSION) this.#setString(target, 'queryStringObfuscation', DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP) this.#setBoolean(target, 'remoteConfig.enabled', DD_REMOTE_CONFIGURATION_ENABLED) target['remoteConfig.pollInterval'] = maybeFloat(DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS) unprocessedTarget['remoteConfig.pollInterval'] = DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS this.#setBoolean(target, 'reportHostname', DD_TRACE_REPORT_HOSTNAME) if (DD_TRACE_RESOURCE_RENAMING_ENABLED !== undefined) { this.#setBoolean(target, 'resourceRenamingEnabled', DD_TRACE_RESOURCE_RENAMING_ENABLED) } // only used to explicitly set runtimeMetrics to false const otelSetRuntimeMetrics = String(OTEL_METRICS_EXPORTER).toLowerCase() === 'none' ? false : undefined this.#setBoolean(target, 'runtimeMetrics.enabled', DD_RUNTIME_METRICS_ENABLED || otelSetRuntimeMetrics) this.#setBoolean(target, 'runtimeMetrics.eventLoop', DD_RUNTIME_METRICS_EVENT_LOOP_ENABLED) this.#setBoolean(target, 'runtimeMetrics.gc', DD_RUNTIME_METRICS_GC_ENABLED) this.#setBoolean(target, 'runtimeMetricsRuntimeId', DD_RUNTIME_METRICS_RUNTIME_ID_ENABLED) this.#setArray(target, 'sampler.spanSamplingRules', reformatSpanSamplingRules( maybeJsonFile(DD_SPAN_SAMPLING_RULES_FILE) ?? safeJsonParse(DD_SPAN_SAMPLING_RULES) )) this.#setUnit(target, 'sampleRate', DD_TRACE_SAMPLE_RATE || getFromOtelSamplerMap(OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG)) target['sampler.rateLimit'] = DD_TRACE_RATE_LIMIT this.#setSamplingRule(target, 'sampler.rules', safeJsonParse(DD_TRACE_SAMPLING_RULES)) unprocessedTarget['sampler.rules'] = DD_TRACE_SAMPLING_RULES this.#setString(target, 'scope', DD_TRACE_SCOPE) this.#setString(target, 'service', DD_SERVICE || tags.service || OTEL_SERVICE_NAME) if (DD_SERVICE_MAPPING) { target.serviceMapping = Object.fromEntries( DD_SERVICE_MAPPING.split(',').map(x => x.trim().split(':')) ) } this.#setString(target, 'site', DD_SITE) if (DD_TRACE_SPAN_ATTRIBUTE_SCHEMA) { this.#setString(target, 'spanAttributeSchema', validateNamingVersion(DD_TRACE_SPAN_ATTRIBUTE_SCHEMA)) unprocessedTarget.spanAttributeSchema = DD_TRACE_SPAN_ATTRIBUTE_SCHEMA } // 0: disabled, 1: logging, 2: garbage collection + logging target.spanLeakDebug = maybeInt(DD_TRACE_SPAN_LEAK_DEBUG) this.#setBoolean(target, 'spanRemoveIntegrationFromService', DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED) this.#setBoolean(target, 'startupLogs', DD_TRACE_STARTUP_LOGS) this.#setTags(target, 'tags', tags) target.tagsHeaderMaxLength = DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH this.#setBoolean(target, 'telemetry.enabled', DD_INSTRUMENTATION_TELEMETRY_ENABLED) this.#setString(target, 'instrumentation_config_id', DD_INSTRUMENTATION_CONFIG_ID) this.#setBoolean(target, 'telemetry.debug', DD_TELEMETRY_DEBUG) this.#setBoolean(target, 'telemetry.dependencyCollection', DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED) target['telemetry.heartbeatInterval'] = maybeInt(Math.floor(DD_TELEMETRY_HEARTBEAT_INTERVAL * 1000)) unprocessedTarget['telemetry.heartbeatInterval'] = DD_TELEMETRY_HEARTBEAT_INTERVAL * 1000 this.#setBoolean(target, 'telemetry.logCollection', DD_TELEMETRY_LOG_COLLECTION_ENABLED) this.#setBoolean(target, 'telemetry.metrics', DD_TELEMETRY_METRICS_ENABLED) this.#setBoolean(target, 'traceId128BitGenerationEnabled', DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED) this.#setBoolean(target, 'traceId128BitLoggingEnabled', DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED) warnIfPropagationStyleConflict( DD_TRACE_PROPAGATION_STYLE, DD_TRACE_PROPAGATION_STYLE_INJECT, DD_TRACE_PROPAGATION_STYLE_EXTRACT ) if (DD_TRACE_PROPAGATION_STYLE !== undefined) { this.#setArray(target, 'tracePropagationStyle.inject', normalizePropagationStyle(DD_TRACE_PROPAGATION_STYLE)) this.#setArray(target, 'tracePropagationStyle.extract', normalizePropagationStyle(DD_TRACE_PROPAGATION_STYLE)) } if (DD_TRACE_PROPAGATION_STYLE_INJECT !== undefined) { this.#setArray(target, 'tracePropagationStyle.inject', normalizePropagationStyle(DD_TRACE_PROPAGATION_STYLE_INJECT)) } if (DD_TRACE_PROPAGATION_STYLE_EXTRACT !== undefined) { this.#setArray(target, 'tracePropagationStyle.extract', normalizePropagationStyle(DD_TRACE_PROPAGATION_STYLE_EXTRACT)) } this.#setBoolean(target, 'tracePropagationExtractFirst', DD_TRACE_PROPAGATION_EXTRACT_FIRST) if (DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT !== undefined) { const stringPropagationBehaviorExtract = String(DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT) target.tracePropagationBehaviorExtract = VALID_PROPAGATION_BEHAVIOR_EXTRACT.has(stringPropagationBehaviorExtract) ? stringPropagationBehaviorExtract : 'continue' } if (DD_TRACE_PROPAGATION_STYLE !== undefined || DD_TRACE_PROPAGATION_STYLE_INJECT !== undefined || DD_TRACE_PROPAGATION_STYLE_EXTRACT !== undefined || OTEL_PROPAGATORS !== undefined) { // At least one var is defined, calculate value using truthy logic const useDdStyle = DD_TRACE_PROPAGATION_STYLE || DD_TRACE_PROPAGATION_STYLE_INJECT || DD_TRACE_PROPAGATION_STYLE_EXTRACT this.#setBoolean(target, 'tracePropagationStyle.otelPropagators', useDdStyle ? false : !!OTEL_PROPAGATORS) // Use OTEL_PROPAGATORS if no DD-specific vars are set if (!useDdStyle && OTEL_PROPAGATORS) { const otelStyles = normalizePropagationStyle(OTEL_PROPAGATORS) // Validate OTEL propagators for (const style of otelStyles || []) { if (!VALID_PROPAGATION_STYLES.has(style)) { log.warn('unexpected value %s for OTEL_PROPAGATORS environment variable', style) getCounter('otel.env.invalid', 'DD_TRACE_PROPAGATION_STYLE', 'OTEL_PROPAGATORS').inc() } } // Set inject/extract from OTEL_PROPAGATORS if (otelStyles) { this.#setArray(target, 'tracePropagationStyle.inject', otelStyles) this.#setArray(target, 'tracePropagationStyle.extract', otelStyles) } } } this.#setBoolean(target, 'traceWebsocketMessagesEnabled', DD_TRACE_WEBSOCKET_MESSAGES_ENABLED) this.#setBoolean(target, 'traceWebsocketMessagesInheritSampling', DD_TRACE_WEBSOCKET_MESSAGES_INHERIT_SAMPLING) this.#setBoolean(target, 'traceWebsocketMessagesSeparateTraces', DD_TRACE_WEBSOCKET_MESSAGES_SEPARATE_TRACES) this.#setBoolean(target, 'tracing', DD_TRACING_ENABLED) this.#setString(target, 'version', DD_VERSION || tags.version) this.#setBoolean(target, 'inferredProxyServicesEnabled', DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED) this.#setBoolean(target, 'trace.aws.addSpanPointers', DD_TRACE_AWS_ADD_SPAN_POINTERS) this.#setString(target, 'trace.dynamoDb.tablePrimaryKeys', DD_TRACE_DYNAMODB_TABLE_PRIMARY_KEYS) this.#setArray(target, 'graphqlErrorExtensions', DD_TRACE_GRAPHQL_ERROR_EXTENSIONS) this.#setBoolean(target, 'trace.nativeSpanEvents', DD_TRACE_NATIVE_SPAN_EVENTS) target['vertexai.spanPromptCompletionSampleRate'] = maybeFloat(DD_VERTEXAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE) target['vertexai.spanCharLimit'] = maybeInt(DD_VERTEXAI_SPAN_CHAR_LIMIT) } #applyOptions (options) { const opts = this.#options const tags = {} options = this.#optionsArg = { ingestion: {}, ...options, ...opts } tagger.add(tags, options.tags) this.#setBoolean(opts, 'apmTracingEnabled', options.apmTracingEnabled ?? (options.experimental?.appsec?.standalone && !options.experimental.appsec.standalone.enabled) ) this.#setBoolean(opts, 'appsec.apiSecurity.enabled', options.appsec?.apiSecurity?.enabled) this.#setBoolean(opts, 'appsec.apiSecurity.endpointCollectionEnabled', options.appsec?.apiSecurity?.endpointCollectionEnabled) opts['appsec.apiSecurity.endpointCollectionMessageLimit'] = maybeInt(options.appsec?.apiSecurity?.endpointCollectionMessageLimit) opts['appsec.blockedTemplateGraphql'] = maybeFile(options.appsec?.blockedTemplateGraphql) opts['appsec.blockedTemplateHtml'] = maybeFile(options.appsec?.blockedTemplateHtml) this.#optsUnprocessed['appsec.blockedTemplateHtml'] = options.appsec?.blockedTemplateHtml opts['appsec.blockedTemplateJson'] = maybeFile(options.appsec?.blockedTemplateJson) this.#optsUnprocessed['appsec.blockedTemplateJson'] = options.appsec?.blockedTemplateJson this.#setBoolean(opts, 'appsec.enabled', options.appsec?.enabled) this.#setString(opts, 'appsec.eventTracking.mode', options.appsec?.eventTracking?.mode) this.#setBoolean( opts, 'appsec.extendedHeadersCollection.enabled', options.appsec?.extendedHeadersCollection?.enabled ) this.#setBoolean( opts, 'appsec.extendedHeadersCollection.redaction', options.appsec?.extendedHeadersCollection?.redaction ) opts['appsec.extendedHeadersCollection.maxHeaders'] = options.appsec?.extendedHeadersCollection?.maxHeaders this.#setString(opts, 'appsec.obfuscatorKeyRegex', options.appsec?.obfuscatorKeyRegex) this.#setString(opts, 'appsec.obfuscatorValueRegex', options.appsec?.obfuscatorValueRegex) this.#setBoolean(opts, 'appsec.rasp.enabled', options.appsec?.rasp?.enabled) this.#setBoolean(opts, 'appsec.rasp.bodyCollection', options.appsec?.rasp?.bodyCollection) opts['appsec.rateLimit'] = maybeInt(options.appsec?.rateLimit) this.#optsUnprocessed['appsec.rateLimit'] = options.appsec?.rateLimit this.#setString(opts, 'appsec.rules', options.appsec?.rules) this.#setBoolean(opts, 'appsec.stackTrace.enabled', options.appsec?.stackTrace?.enabled) opts['appsec.stackTrace.maxDepth'] = maybeInt(options.appsec?.stackTrace?.maxDepth) this.#optsUnprocessed['appsec.stackTrace.maxDepth'] = options.appsec?.stackTrace?.maxDepth opts['appsec.stackTrace.maxStackTraces'] = maybeInt(options.appsec?.stackTrace?.maxStackTraces) this.#optsUnprocessed['appsec.stackTrace.maxStackTraces'] = options.appsec?.stackTrace?.maxStackTraces opts['appsec.wafTimeout'] = maybeInt(options.appsec?.wafTimeout) this.#optsUnprocessed['appsec.wafTimeout'] = options.appsec?.wafTimeout this.#setBoolean(opts, 'clientIpEnabled', options.clientIpEnabled) this.#setString(opts, 'clientIpHeader', options.clientIpHeader?.toLowerCase()) if (options.cloudPayloadTagging?.request || options.cloudPayloadTagging?.response) { if (options.cloudPayloadTagging.request) { this.#setBoolean(opts, 'cloudPayloadTagging.requestsEnabled', true) } if (options.cloudPayloadTagging.response) { this.#setBoolean(opts, 'cloudPayloadTagging.responsesEnabled', true) } opts['cloudPayloadTagging.rules'] = appendRules( splitJSONPathRules(options.cloudPayloadTagging.request), splitJSONPathRules(options.cloudPayloadTagging.response) ) } if (options.cloudPayloadTagging?.requestsEnabled !== undefined) { this.#setBoolean(opts, 'cloudPayloadTagging.requestsEnabled', options.cloudPayloadTagging.requestsEnabled) } if (options.cloudPayloadTagging?.responsesEnabled !== undefined) { this.#setBoolean(opts, 'cloudPayloadTagging.responsesEnabled', options.cloudPayloadTagging.responsesEnabled) } opts['cloudPayloadTagging.maxDepth'] = maybeInt(options.cloudPayloadTagging?.maxDepth) opts.baggageMaxBytes = options.baggageMaxBytes opts.baggageMaxItems = options.baggageMaxItems opts.baggageTagKeys = options.baggageTagKeys this.#setBoolean(opts, 'codeOriginForSpans.enabled', options.codeOriginForSpans?.enabled) this.#setBoolean( opts, 'codeOriginForSpans.experimental.exit_spans.enabled', options.codeOriginForSpans?.experimental?.exit_spans?.enabled ) this.#setString(opts, 'dbmPropagationMode', options.dbmPropagationMode) if (options.dogstatsd) { this.#setString(opts, 'dogstatsd.hostname', options.dogstatsd.hostname) this.#setString(opts, 'dogstatsd.port', options.dogstatsd.port) } this.#setBoolean(opts, 'dsmEnabled', options.dsmEnabled) opts['dynamicInstrumentation.captureTimeoutMs'] = maybeInt(options.dynamicInstrumentation?.captureTimeoutMs) this.#optsUnprocessed['dynamicInstrumentation.captureTimeoutMs'] = options.dynamicInstrumentation?.captureTimeoutMs this.#setBoolean(opts, 'dynamicInstrumentation.enabled', options.dynamicInstrumentation?.enabled) this.#setString(opts, 'dynamicInstrumentation.probeFile', options.dynamicInstrumentation?.probeFile) this.#setArray( opts, 'dynamicInstrumentation.redactedIdentifiers', options.dynamicInstrumentation?.redactedIdentifiers ) this.#setArray( opts, 'dynamicInstrumentation.redactionExcludedIdentifiers', options.dynamicInstrumentation?.redactionExcludedIdentifiers ) opts['dynamicInstrumentation.uploadIntervalSeconds'] = maybeFloat(options.dynamicInstrumentation?.uploadIntervalSeconds) this.#optsUnprocessed['dynamicInstrumentation.uploadIntervalSeconds'] = options.dynamicInstrumentation?.uploadIntervalSeconds this.#setString(opts, 'env', options.env || tags.env) this.#setBoolean(opts, 'experimental.aiguard.enabled', options.experimental?.aiguard?.enabled) this.#setString(opts, 'experimental.aiguard.endpoint', options.experimental?.aiguard?.endpoint) opts['experimental.aiguard.maxMessagesLength'] = maybeInt(options.experimental?.aiguard?.maxMessagesLength) this.#optsUnprocessed['experimental.aiguard.maxMessagesLength'] = options.experimental?.aiguard?.maxMessagesLength opts['experimental.aiguard.maxContentSize'] = maybeInt(options.experimental?.aiguard?.maxContentSize) this.#optsUnprocessed['experimental.aiguard.maxContentSize'] = options.experimental?.aiguard?.maxContentSize opts['experimental.aiguard.timeout'] = maybeInt(options.experimental?.aiguard?.timeout) this.#optsUnprocessed['experimental.aiguard.timeout'] = options.experimental?.aiguard?.timeout this.#setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData) this.#setString(opts, 'experimental.exporter', options.experimental?.exporter) this.#setBoolean(opts, 'experimental.flaggingProvider.enabled', options.experimental?.flaggingProvider?.enabled) opts.flushInterval = maybeInt(options.flushInterval) this.#optsUnprocessed.flushInterval = options.flushInterval opts.flushMinSpans = maybeInt(options.flushMinSpans) this.#optsUnprocessed.flushMinSpans = options.flushMinSpans this.#setArray(opts, 'headerTags', options.headerTags) this.#setString(opts, 'hostname', options.hostname)