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/workflows/platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ jobs:
# We'll run these separately for earlier (i.e. unsupported) versions
integration-guardrails:
strategy:
fail-fast: false
matrix:
version: [14.0.0, 14, 16.0.0, 18.0.0, 20.0.0, 22.0.0, 24.0.0]
runs-on: ubuntu-latest
Expand All @@ -462,6 +463,7 @@ jobs:

integration-guardrails-unsupported:
strategy:
fail-fast: false
matrix:
version: ['0.8', '0.10', '0.12', '4', '6', '8', '10', '12']
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/update-3rdparty-licenses.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
EOF

- name: Regenerate LICENSE-3rdparty.csv
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
dd-license-attribution generate-sbom-csv \
--use-mirrors=mirrors.json \
Expand Down
12 changes: 12 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,16 @@
// Use the workspace version of TypeScript instead of VSCode's bundled version
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"cSpell.words": [
"aerospike",
"appsec",
"backportability",
"kafkajs",
"llmobs",
"microbenchmarks",
"oracledb",
"rabbitmq",
"rspack",
"sirun"
],
}
290 changes: 290 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
# AGENTS.md

## Prerequisites

- Node.js >= 18
- yarn 1.x
- Docker + docker-compose (for running service dependencies in tests)

## Setup

- Install dependencies: `yarn install`

**Note:** This project uses yarn, not npm. Always use `yarn` commands instead of `npm` commands.

## Project Overview

dd-trace is the Datadog client library for Node.js.

**Key Directories:**
- `packages/dd-trace/` - Main library (APM, profiling, debugger, appsec, llmobs, CI visibility)
- `packages/datadog-core/` - Async context storage, shared utilities
- `packages/datadog-instrumentations/` - Instrumentation implementations
- `packages/datadog-plugin-*/` - 100+ plugins for third-party integrations
- `integration-tests/` - E2E integration tests
- `benchmark/` - Performance benchmarks

## Testing Instructions

### Testing Workflow

When developing a feature or fixing a bug:

1. Start with individual test files to verify things work
2. Run component tests: `yarn test:<component>` (e.g., `yarn test:debugger`, `yarn test:appsec`)
3. Run integration tests: `yarn test:integration:<component>` (e.g., `yarn test:integration:debugger`)

### Running Individual Tests

**IMPORTANT**: Never run `yarn test` directly. Use `mocha` or `tap` directly on test files.

**Mocha unit tests:**
```bash
./node_modules/.bin/mocha -r "packages/dd-trace/test/setup/mocha.js" path/to/test.spec.js
```

**Tap unit tests:**
```bash
./node_modules/.bin/tap path/to/test.spec.js
```

**Integration tests:**
```bash
./node_modules/.bin/mocha --timeout 60000 -r "packages/dd-trace/test/setup/core.js" path/to/test.spec.js
```

**Target specific tests:**
- Add `--grep "test name pattern"` flag

**Enable debug logging:**
- Prefix with `DD_TRACE_DEBUG=true`

**Note**: New tests should be written using mocha, not tap. Existing tap tests use mocha-style `describe` and `it` blocks.

### Plugin Tests

**Use `PLUGINS` env var:**
```bash
PLUGINS="amqplib" yarn test:plugins
PLUGINS="amqplib|bluebird" yarn test:plugins # pipe-delimited for multiple
./node_modules/.bin/mocha -r "packages/dd-trace/test/setup/mocha.js" packages/datadog-plugin-amqplib/test/index.spec.js
```

**With external services** (check `.github/workflows/apm-integrations.yml` for `SERVICES`):
```bash
export SERVICES="rabbitmq" PLUGINS="amqplib"
docker compose up -d $SERVICES
yarn services && yarn test:plugins
```

**ARM64 incompatible:** `aerospike`, `couchbase`, `grpc`, `oracledb`

### Test Coverage

```bash
./node_modules/.bin/nyc --include "packages/dd-trace/src/debugger/**/*.js" \
./node_modules/.bin/mocha -r "packages/dd-trace/test/setup/mocha.js" \
"packages/dd-trace/test/debugger/**/*.spec.js"
```

**Philosophy:**
- Integration tests (running in sandboxes) don't count towards nyc coverage metrics
- Don't add redundant unit tests solely to improve coverage numbers
- Focus on covering important production code paths with whichever test type makes sense

### Test Assertions

Use `node:assert/strict` for standard assertions. For partial deep object checks, use `assertObjectContains` from `integration-tests/helpers/index.js`:

```js
const assert = require('node:assert/strict')
const { assertObjectContains } = require('../helpers')

assert.equal(actual, expected)
assertObjectContains(response, { status: 200, body: { user: { name: 'Alice' } } })
```

### Time-Based Testing

**Never rely on actual time passing in unit tests.** Use sinon's fake timers to mock time and make tests deterministic and fast.

## Code Style & Linting

### Linting & Naming
- Lint: `yarn lint` / `yarn lint:fix`
- Files: kebab-case
- JSDoc: TypeScript-compatible syntax (`@param {string}`, `@returns {Promise<void>}`, `@typedef`)

### Import Ordering

Separate groups with empty line, sort alphabetically within each:
1. Node.js core modules (with `node:` prefix)
2. Third-party modules
3. Internal imports (by path proximity, then alpha)

Use destructuring for utility modules when appropriate.

```js
const fs = require('node:fs')
const path = require('node:path')

const express = require('express')

const { myConf } = require('./config')
const log = require('../log')
```

### ECMAScript and Node.js API Standards

**Target Node.js 18.0.0 compatibility:**
- Use modern JS features supported by Node.js (e.g., optional chaining `?.`, nullish coalescing `??`)
- Guard newer APIs with version checks using [`version.js`](./version.js):
```js
const { NODE_MAJOR } = require('./version')
if (NODE_MAJOR >= 20) { /* Use Node.js 20+ API */ }
```
- **Prefix Node.js core modules with `node:`** (e.g., `require('node:assert')`)

### Performance and Memory

**CRITICAL: Tracer runs in application hot paths - every operation counts.**

**Async/Await:**
- Do NOT use `async/await` or promises in production code (npm package)
- Allowed ONLY in: test files, worker threads (e.g., `packages/dd-trace/src/debugger/devtools_client/`)
- Use callbacks or synchronous patterns instead

**Memory:**
- Minimize allocations in frequently-called paths
- Avoid unnecessary objects, closures, arrays
- Reuse objects and buffers
- Minimize GC pressure

#### Array Iteration

**Prefer `for-of`, `for`, `while` loops over functional methods (`map()`, `forEach()`, `filter()`):**
- Avoid `items.forEach(item => process(item))` → use `for (const item of items) { process(item) }`
- Avoid chaining `items.filter(...).map(...)` → use single loop with conditional push
- Functional methods create closures and intermediate arrays

**Functional methods acceptable in:**
- Test files
- Non-hot-path code where readability benefits
- One-time initialization code

**Loop selection:**
- `for-of` - Simple iteration
- `for` with index - Need index or better performance in hot paths
- `while` - Custom iteration logic

### Debugging and Logging

Use `log` module (`packages/dd-trace/src/log/index.js`) with printf-style formatting (not template strings):

```js
const log = require('../log')
log.debug('Value: %s', someValue) // printf-style
log.debug(() => `Expensive: ${expensive()}`) // callback for expensive ops
log.error('Error: %s', msg, err) // error as last arg
```

Enable: `DD_TRACE_DEBUG=true DD_TRACE_LOG_LEVEL=info node app.js`
Levels: `trace`, `debug`, `info`, `warn`, `error`

### Error Handling

**Never crash user apps:** Catch/log errors (`log.error()`/`log.warn()`), resume or disable plugin/subsystem
Avoid try/catch in hot paths - validate inputs early

## Development Workflow

### Core Principles
- **Search first**: Check for existing utilities/patterns before creating new code
- **Small PRs**: Break large efforts into incremental, reviewable changes
- **Descriptive code**: Self-documenting with verbs in function names; comment when needed
- **Readable formatting**: Empty lines for grouping, split complex objects, extract variables
- **Avoid large refactors**: Iterative changes, gradual pattern introduction
- **Test changes**: Test logic (not mocks), failure cases, edge cases - always update tests

### Always Consider Backportability

**We always backport `master` to older versions.**
- Keep breaking changes to a minimum
- Don't use language/runtime features that are too new
- **Guard breaking changes with version checks** using [`version.js`](./version.js):
```js
const { DD_MAJOR } = require('./version')
if (DD_MAJOR >= 6) {
// New behavior for v6+
} else {
// Old behavior for v5 and earlier
}
```

## Adding New Configuration Options

1. **Add default value** in `packages/dd-trace/src/config_defaults.js`
2. **Map environment variable** in `packages/dd-trace/src/config.js` (`#applyEnvironment()` method)
3. **Add TypeScript definitions** in `index.d.ts`
4. **Add to telemetry name mapping** (if applicable) in `packages/dd-trace/src/telemetry/telemetry.js`
5. **Update** `packages/dd-trace/src/supported-configurations.json`
6. **Document** in `docs/API.md` (non-internal/experimental options only)
7. **Add tests** in `packages/dd-trace/test/config.spec.js`

**Naming Convention:** Size/time-based config options should have unit suffixes (e.g., `timeoutMs`, `maxBytes`, `intervalSeconds`).

## Adding New Instrumentation

**New instrumentations go in `packages/datadog-instrumentations/`.** The instrumentation system uses diagnostic channels for communication.

Many integrations have corresponding plugins in `packages/datadog-plugin-*/` that work with the instrumentation layer.

### What Are Plugins?

Plugins are modular code components in `packages/datadog-plugin-*/` directories that:
- Subscribe to diagnostic channels to receive instrumentation events
- Handle APM tracing logic (spans, metadata, error tracking)
- Manage feature-specific logic (e.g., code origin tracking, LLM observability)

**Plugin Base Classes:**
- **`Plugin`** - Base class with diagnostic channel subscription, storage binding, enable/disable lifecycle. Use for non-tracing functionality.
- **`TracingPlugin`** - Extends `Plugin` with APM tracing helpers (`startSpan()`, automatic trace events, `activeSpan` getter). Use for plugins creating trace spans.
- **`CompositePlugin`** - Extends `Plugin` to compose multiple sub-plugins. Use when one integration needs multiple feature plugins (e.g., `express` combines tracing and code origin plugins).

**Plugin Loading:**
- Plugins load lazily when application `require()`s the corresponding library
- Disable with `DD_TRACE_DISABLED_PLUGINS` or `DD_TRACE_<PLUGIN>_ENABLED=false`
- Test framework plugins only load when Test Optimization mode (`isCiVisibility`) is enabled

**When to Create a New Plugin:**
1. Adding support for a new third-party library/framework
2. Adding a new product feature that integrates with existing libraries (use `CompositePlugin`)

### Creating a New Plugin

```bash
mkdir -p packages/datadog-plugin-<name>/{src,test}
cp packages/datadog-plugin-kafkajs/src/index.js packages/datadog-plugin-<name>/src/
```

Edit `src/index.js`, create `test/index.spec.js`, then register in:
`packages/dd-trace/src/plugins/index.js`, `index.d.ts`, `docs/test.ts`, `docs/API.md`, `.github/workflows/apm-integrations.yml`

## Pull Requests and CI

### Commit Messages

Conventional format: `type(scope): description`
Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `ci`
Example: `feat(appsec): add new WAF rule`

### PR Requirements

- Use template from `.github/pull_request_template.md`
- Label: `semver-patch` (fixes only), `semver-minor` (new features), `semver-major` (breaking)
- **All tests must pass - all-green policy, no exceptions**

## Vendoring Dependencies

Using rspack: Run `yarn` in `vendor/` to install/bundle dependencies → `packages/node_modules/`
(Some deps excluded, e.g., `@opentelemetry/api`)
1 change: 1 addition & 0 deletions CLAUDE.md
6 changes: 6 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@
/packages/dd-trace/src/llmobs/ @DataDog/ml-observability
/packages/dd-trace/test/llmobs/ @DataDog/ml-observability

# Data Streams Monitoring
/packages/dd-trace/src/datastreams/ @DataDog/data-streams-monitoring
/packages/dd-trace/test/datastreams/ @DataDog/data-streams-monitoring
/packages/**/dsm.spec.js @DataDog/data-streams-monitoring
/packages/**/*.dsm.spec.js @DataDog/data-streams-monitoring

# API SDK
/packages/dd-trace/src/telemetry/ @DataDog/apm-sdk-capabilities-js
/packages/dd-trace/test/telemetry/ @DataDog/apm-sdk-capabilities-js
Expand Down
Loading