Skip to content
Draft
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: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@
# Language Platform
/* @DataDog/lang-platform-js

/eslint-rules/ @DataDog/lang-platform-js

/integration-tests/bun/ @DataDog/lang-platform-js
/integration-tests/init.spec.js @DataDog/lang-platform-js
/integration-tests/package-guardrails.spec.js @DataDog/lang-platform-js
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ jobs:
- uses: ./.github/actions/install
- run: npm run verify-exercised-tests

generated-config-types:
runs-on: ubuntu-latest
name: Generated config types
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/node/latest
- run: npm run verify:config:types

workflow-job-names:
runs-on: ubuntu-latest
name: Workflow job names (unique)
Expand Down
241 changes: 241 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -466,3 +466,244 @@ $ yarn bench
```

[1]: https://docs.datadoghq.com/help

## Working with Configurations

`packages/dd-trace/src/config/supported-configurations.json` is the source of truth for tracer configuration metadata.

When you add a new configuration here, the config system can usually derive:

- default values
- env var parsing
- `tracer.init({...})` option mapping
- generated config types
- config telemetry

## What A Developer Needs To Know

Each entry defines:

- the canonical env var name
- the runtime type
- the default value
- the programmatic option path
- optional aliases, validation, and transforms

Minimal example:

```json
"DD_AGENT_HOST": [{
"implementation": "E",
"type": "string",
"configurationNames": ["hostname"],
"default": "127.0.0.1",
"aliases": ["DD_TRACE_AGENT_HOSTNAME"]
}]
```

Important fields:

- `type`: parser to use for environment variables. Common values are `string`, `boolean`, `integer`, `decimal`, `array`, `map`, `json`.
- `default`: parsed into the runtime type. `null` means `undefined` at runtime.
- `configurationNames`: programmatic option names. The first entry becomes the main internal property path.
- `internalPropertyName`: use this instead of `configurationNames` when the runtime property path should differ from the public option name.
- `transform`: extra conversion after parsing. This applies to both env vars and programmatic options.
- `allowed`: whitelist of accepted values.
- `aliases`: old or alternate env var names.
- `deprecated`: emits a deprecation warning when used.
- `description`: developer-facing note in the JSON.
- `implementation`: metadata only in the current flow.

## Runtime Flow

```mermaid
flowchart LR
A["supported-configurations.json"] --> B["defaults.js<br/>build defaults + lookup tables"]
A --> C["helper.js<br/>aliases + deprecations"]
A --> D["generate-config-types.js<br/>generated-config-types.d.ts"]
B --> E["config/index.js"]
C --> E
E --> F["Config singleton"]
E --> G["Config telemetry"]
H["remote_config.js"] --> E
```

Load order in `config/index.js`:

1. defaults
2. local stable config
3. env vars
4. fleet stable config
5. `tracer.init({...})` options
6. calculated values

## Examples That Matter

### Simple boolean

```json
"DD_RUNTIME_METRICS_ENABLED": [{
"type": "boolean",
"configurationNames": ["runtimeMetrics.enabled", "runtimeMetrics"],
"default": "false"
}]
```

Both of these work:

```js
tracer.init({ runtimeMetrics: true })
```

```js
tracer.init({
runtimeMetrics: {
enabled: true
}
})
```

Result:

```js
config.runtimeMetrics.enabled === true
```

### Decimal with transform

```json
"DD_TRACE_SAMPLE_RATE": [{
"type": "decimal",
"configurationNames": ["sampleRate", "ingestion.sampleRate"],
"default": null,
"transform": "sampleRate"
}]
```

The `sampleRate` transform validates and clamps the value to the supported `0..1` range.

### Array with transform

```json
"DD_TRACE_HEADER_TAGS": [{
"type": "array",
"configurationNames": ["headerTags"],
"default": "",
"transform": "stripColonWhitespace"
}]
```

This matters because the transform is reused for both input styles:

```bash
DD_TRACE_HEADER_TAGS="x-user-id : user.id, x-team : team"
```

```js
tracer.init({
headerTags: ['x-user-id : user.id', 'x-team : team']
})
```

Both become:

```js
config.headerTags
// ['x-user-id:user.id', 'x-team:team']
```

### JSON with nested output

```json
"DD_TRACE_SAMPLING_RULES": [{
"type": "json",
"configurationNames": ["samplingRules"],
"default": "[]",
"transform": "toCamelCase"
}]
```

```bash
DD_TRACE_SAMPLING_RULES='[{"sample_rate":0.5,"service":"api"}]'
```

Result:

```js
config.samplingRules
// [{ sampleRate: 0.5, service: 'api' }]
```

### Internal property path

```json
"DD_API_KEY": [{
"type": "string",
"default": null,
"internalPropertyName": "apiKey"
}]
```

Result:

```js
config.apiKey
```

## Nested Properties

Dot notation creates nested objects on the config singleton.

```json
"DD_API_SECURITY_ENABLED": [{
"type": "boolean",
"configurationNames": [
"appsec.apiSecurity.enabled",
"experimental.appsec.apiSecurity.enabled"
],
"default": "true"
}]
```

```js
tracer.init({
appsec: {
apiSecurity: {
enabled: true
}
}
})
```

Result:

```js
config.appsec.apiSecurity.enabled === true
```

## Telemetry And Remote Config

Config telemetry is handled automatically by the standard config flow.

If your config is defined in `supported-configurations.json` and goes through the normal parsing/application path, telemetry usually works without extra code. Telemetry records the canonical name, the normalized value, and the origin such as `default`, `env_var`, `code`, `remote_config`, or `calculated`.

Remote config is not a separate system. `packages/dd-trace/src/config/remote_config.js` translates remote field names into local option names and then applies them through `config.setRemoteConfig(...)`. After that, the normal pipeline runs again: apply options, recompute calculated values, and update telemetry.

## Adding A New Configuration

Use this checklist:

1. Add the new entry to `packages/dd-trace/src/config/supported-configurations.json`.
2. Pick the correct `type` and `default`.
3. Add `configurationNames` if the setting should be exposed via `tracer.init({...})`. Add the documentation to `index.d.ts`.
4. Use `internalPropertyName` if the runtime property path should differ.
5. Add `transform` or `allowed` only if the raw parsed value is not enough.
6. Add `aliases` or `deprecated` only for compatibility.
7. Regenerate types if needed.
8. Add tests for env vars, programmatic options, and edge cases.

## Mental Model

Think of `supported-configurations.json` as the schema for one config singleton.

You describe the input shape once, and the runtime uses that to build defaults, parse env vars, map programmatic options, generate types, and emit telemetry.
2 changes: 1 addition & 1 deletion benchmark/sirun/exporting-pipeline/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const SpanProcessor = require('../../../packages/dd-trace/src/span_processor')
const Exporter = require('../../../packages/dd-trace/src/exporters/agent/index')
const PrioritySampler = require('../../../packages/dd-trace/src/priority_sampler')
const id = require('../../../packages/dd-trace/src/id')
const defaults = require('../../../packages/dd-trace/src/config/defaults')
const { defaults } = require('../../../packages/dd-trace/src/config/defaults')

const config = {
url: `http://${defaults.hostname}:${defaults.port}`,
Expand Down
2 changes: 1 addition & 1 deletion benchmark/sirun/statsd.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

const dgram = require('dgram')
const defaults = require('../../packages/dd-trace/src/config/defaults')
const { defaults } = require('../../packages/dd-trace/src/config/defaults')
const port = process.env.SIRUN_STATSD_PORT || defaults['dogstatsd.port']

class StatsD {
Expand Down
Loading
Loading