Skip to main content

apm-integrations

Use when adding, debugging, fixing, or modifying instrumentation and plugins for third-party libraries in dd-trace-js. Triggers: "add a new integration", "instrument a library", any *Plugin base class (Tracing/Database/Cache/ Client/Server/Consumer/Producer/Composite), "addHook", "shimmer.wrap", "orchestrion", "bindStart"/"bindFinish", "diagnostic channel", "runStores", "subscriber cardinality", "channel.publish gate", "read upstream source", "reference plugin".

설치로 이동

소스 정보

저장소
DataDog/dd-trace-js
최근 소스 활동
2026년 9월 11일 17:03
감지된 SKILL.md 언어
영어
스타
835
포크
407

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
6 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
apm-integrations
description
Use when adding, debugging, fixing, or modifying instrumentation and plugins for third-party libraries in dd-trace-js. Triggers: "add a new integration", "instrument a library", any *Plugin base class (Tracing/Database/Cache/ Client/Server/Consumer/Producer/Composite), "addHook", "shimmer.wrap", "orchestrion", "bindStart"/"bindFinish", "diagnostic channel", "runStores", "subscriber cardinality", "channel.publish gate", "read upstream source", "reference plugin".
# APM Integrations dd-trace-js provides automatic tracing for 100+ third-party libraries. Each integration consists of two decoupled layers communicating via Node.js diagnostic channels. ## Architecture ```text ┌──────────────────────────┐ diagnostic channels ┌─────────────────────────┐ │ Instrumentation │ ──────────────────────────▶ │ Plugin │ │ datadog-instrumentations │ apm:<name>:<op>:start │ datadog-plugin-<name> │ │ │ apm:<name>:<op>:finish │ │ │ Hooks into library │ apm:<name>:<op>:error │ Creates spans, sets │ │ methods, emits events │ │ tags, handles errors │ └──────────────────────────┘ └─────────────────────────┘ ``` `finish` above is the legacy manual-channel completion event. `tracingChannel` and Orchestrion use `end` / `asyncEnd`, as described below. **Instrumentation** (`packages/datadog-instrumentations/src/`): Hooks into a library's internals and publishes events with context data to named diagnostic channels. Has zero knowledge of tracing — only emits events. **Plugin** (`packages/datadog-plugin-<name>/src/`): Subscribes to diagnostic channel events and creates APM spans with service name, resource, tags, and error metadata. Extends a base class providing lifecycle management. Both layers are always needed for a new integration. ## Instrumentation: Orchestrion First **Orchestrion is the required default when the work exists as a source function.** It rewrites matched CJS/ESM source from JavaScript config, avoiding runtime monkey-patching and ESM's static-binding traps. Start there for top-level declarations, class/object methods, named expressions, and assignments to named receivers. Use shimmer only when the work is created entirely at runtime or the required argument/result mutation cannot happen from Orchestrion's subscriber lifecycle. Config lives in `packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/<name>.js`. See [Orchestrion Reference](references/orchestrion.md) for the full config format and examples. ### When Shimmer Is Necessary Instead Shimmer (`addHook` + `shimmer.wrap`) should **only** be used when orchestrion cannot handle the pattern. When using shimmer, **always include a code comment explaining why orchestrion is not viable.** Valid reasons: - **Dynamic method interception** — methods created at runtime or on prototype chains that orchestrion's static analysis cannot reach - **Factory results that cannot be substituted** — `end` can replace synchronous results and `asyncEnd` can replace native-Promise results; shimmer remains necessary for Promise subclasses, userland thenables, or APIs that require the original result's identity - **Pre-lifecycle argument modification** — arguments must be changed before Orchestrion's `bindStart` / subscribers can run If none of these apply, use orchestrion. For shimmer patterns, refer to existing shimmer-based instrumentations in the codebase (e.g., `packages/datadog-instrumentations/src/pg.js`). Always try to use Orchestrion when beginning a new integration! ## Plugin Base Classes Plugins extend a base class matching the library type. The base class provides automatic channel subscriptions, span lifecycle, and type-specific tags. ```text Plugin ├── CompositePlugin — Multiple sub-plugins (produce + consume) ├── LogPlugin — Log correlation injection (no spans) ├── WebPlugin — Base web plugin │ └── RouterPlugin — Web frameworks with middleware └── TracingPlugin — Base for all span-creating plugins ├── InboundPlugin — Inbound calls │ ├── ServerPlugin — HTTP servers │ └── ConsumerPlugin — Message consumers (DSM) └── OutboundPlugin — Outbound calls ├── ProducerPlugin — Message producers (DSM) └── ClientPlugin — HTTP/RPC clients └── StoragePlugin — Storage systems ├── DatabasePlugin — Database clients (DBM, db.* tags) └── CachePlugin — Key-value caches ``` **Wrong base class = complex workarounds.** Always match the library type to the base class. ## Read Upstream Source First Touching `packages/datadog-instrumentations/src/<lib>.js`, its plugin counterpart, or any orchestrion config — for any reason — read the upstream library's source first. Memory of an SDK's contract drifts faster than the SDK; comments in the wrap go stale every minor version; cross-version diffs surface contract changes guessing misses (lazy → eager attachment, mode-exclusive APIs, new error paths). Two ways to fetch the source locally: 1. **Shallow clone** the installed version: ```bash git clone --depth 1 --branch v<x.y.z> https://github.com/<org>/<repo>.git /tmp/<lib>-versions/v<x.y.z> ``` 1. **`npm pack`** when the published runtime artifact is what matters: ```bash cd /tmp/<lib>-versions && npm pack <lib>@<x.y.z> tar -xzf <lib>-<x.y.z>.tgz -C v<x.y.z> --strip-components=1 ``` Read the file the wrap hooks, the base classes the hooked methods inherit from, and files the wrap doesn't currently touch — a public method, an internal channel, or a metadata field the current instrumentation skipped often gives a cleaner hook (e.g., kafka `cluster.brokerPool.metadata.clusterId`, couchbase `tracingChannel`). ## Key Concepts ### The `ctx` Object Context flows from instrumentation to plugin: - **Orchestrion**: automatically provides `ctx.arguments` (method args) and `ctx.self` (instance) - **Shimmer**: instrumentation sets named properties (`ctx.sql`, `ctx.client`, etc.) - **Plugin sets**: `ctx.currentStore` (span), `ctx.parentStore` (parent span) - **On completion**: `ctx.result` or `ctx.error` ### Channel Event Lifecycle - `runStores()` for **start** events — establishes async context (always) - `publish()` for **completion/error** events — notification only - `hasSubscribers` guard — skip publish/subscriber work when no plugin listens; orchestrion still pays wrapper setup in current templates - When shimmer is necessary, prefer `tracingChannel` (from `dc-polyfill`) over manual channels — it provides `start/end/asyncStart/asyncEnd/error` events automatically ### Channel Prefix Patterns - **Orchestrion**: `tracing:orchestrion:<npm-package>:<channelName>` (set via `static prefix`) - **Shimmer + `tracingChannel`** (preferred): `tracing:apm:<name>:<operation>` (set via `static prefix`) - **Shimmer + manual channels** (legacy): `apm:{id}:{operation}` (default, no `static prefix` needed) ### `bindStart` and completion handlers Use `bindStart` to create the span and return its store. Finish in the event the instrumentation emits: usually `end` for synchronous work, `asyncEnd` for promises/callbacks, and `finish` only for legacy instrumentations that publish it. Orchestrion does not publish `finish`. ### Subscriber Cardinality (`channel.publish` position) When relocating a `channel.publish` call behind a dedupe gate, depth filter, cache-hit return, or any short-circuit, the question is not *"is the publish still there?"* but *"what cardinality does each downstream subscriber need?"*. Subscribers split into two camps that look identical from inside the publish site: - **Once per first occurrence** — tracing plugins that dedupe spans, distinct-path metrics. Safe behind a dedupe gate. - **Once per call** — IAST taint-tracking (mutates each call's `args` object by reference), AppSec WAF subscribers that block/log per invocation, anything walking payload identity. Drops data silently when cardinality falls below one-per-call. Before adding or moving a gate in front of a publish, grep the repo for the channel name, list its subscribers, decide per-subscriber whether the new position preserves the cardinality each needs. When cardinalities diverge, split the publish into a pre-gate (per-call) and a post-gate (per-first-occurrence) call. ## Reference Integrations **Always read 1-2 references of the same type before writing or modifying code.** | Library Type | Plugin | Instrumentation | Base Class | | --- | --- | --- | --- | | Database | `datadog-plugin-pg` | `src/pg.js` | `DatabasePlugin` | | Cache | `datadog-plugin-redis` | `src/redis.js` | `CachePlugin` | | HTTP client | `datadog-plugin-fetch` | `src/fetch.js` | `HttpClientPlugin` (extends `ClientPlugin`) | | Web framework | `datadog-plugin-express` | `src/express.js` | `RouterPlugin` | | Message queue | `datadog-plugin-kafkajs` | `src/kafkajs.js` | `Producer`/`ConsumerPlugin` | | Orchestrion | `datadog-plugin-langchain` | `rewriter/instrumentations/langchain.js` | `TracingPlugin` | For the complete list by base class, see [Reference Plugins](references/reference-plugins.md). ## Debugging - `DD_TRACE_DEBUG=true` to see channel activity - Log `Object.keys(ctx)` in `bindStart` to inspect available context - Spans missing → verify `hasSubscribers` guard; check channel names match between layers - Context lost → ensure `runStores()` (not `publish()`) for start events - ESM fails but CJS works → check `esmFirst: true` in hooks.js (or switch to orchestrion) ## Implementation Workflow Follow these steps when creating or modifying an integration: 1. **Investigate** — Read the upstream library's source (see [Read Upstream Source First](#read-upstream-source-first)). Read 1-2 reference integrations of the same type (see table above). Understand the instrumentation and plugin patterns before writing code. 2. **Implement instrumentation** — Create the instrumentation in `packages/datadog-instrumentations/src/`. Use orchestrion for instrumentation. 3. **Implement plugin** — Create the plugin in `packages/datadog-plugin-<name>/src/`. Extend the correct base class. 4. **Register** — Add entries in `packages/dd-trace/src/plugins/index.js`, every supported public TypeScript surface, `docs/test.ts`, `docs/API.md`, and `.github/workflows/apm-integrations.yml`. 5. **Write tests** — Add unit tests and ESM integration tests. See [Testing](references/testing.md) for templates. 6. **Run tests** — Validate with: ```bash # Run plugin tests (preferred CI command — handles yarn services automatically) PLUGINS="<name>" npm run test:plugins:ci # If the plugin needs external services (databases, message brokers, etc.), # check docker-compose.yml for available service names, then: docker compose up -d <service> SERVICES="<service>" PLUGINS="<name>" npm run test:plugins:ci ``` 7. **Verify** — Confirm all tests pass before marking work as complete. ## Reference Files - **[New Integration Guide](references/new-integration-guide.md)** — Step-by-step guide and checklist for creating a new integration end-to-end - **[Orchestrion Reference](references/orchestrion.md)** — JavaScript config format, channel naming, function kinds, plugin subscription - **[Plugin Patterns](references/plugin-patterns.md)** — `startSpan()` API, `ctx` object details, `CompositePlugin`, channel subscriptions, code style - **[Testing](references/testing.md)** — Unit test and ESM integration test templates - **[Reference Plugins](references/reference-plugins.md)** — All plugins organized by base class
GitHub에서 보기