Open-Source Wikis

/

Datadog Agent

/

Systems

/

Components framework

DataDog/datadog-agent

Components framework

Active contributors: David Ortiz, Pierre Gimalac, Gabriel Dos Santos

Purpose

comp/ is the Datadog Agent's component framework: a structured way to break the codebase into small, testable units with explicit dependencies. It is built on top of go.uber.org/fx.

In 2026 the framework hosts roughly 48 top-level bundles and many hundreds of components. New code is expected to live in comp/. Older pkg/ code is being incrementally migrated.

Why it exists

Before the component framework, most Agent code lived in pkg/ and used global state (singletons, init() functions, package-level variables). That made the system hard to test in isolation and made the build dependencies non-obvious.

The framework solves this by:

  • Replacing globals with interfaces that are injected via Fx.
  • Making lifecycle explicit — every component has an OnStart/OnStop hook.
  • Making bundles the unit of composition: a binary picks the bundles it wants, and the right components fall out automatically.
  • Standardizing mocks for tests.

Layout

A canonical component:

comp/<bundle>/<component>/
├── def/                # Component interface (no implementation)
│   └── component.go    # The exported `type Component interface { ... }`
├── impl/               # Production implementation
│   └── component.go
├── fx/                 # Fx options that wire impl + interface together
│   └── fx.go
└── mock/               # Mock implementation for tests
    └── mock.go

A bundle (comp/<bundle>/bundle.go) lists the components it owns and exposes a single fx.Option that downstream code imports.

comp/README.md is generated from package docs (dda inv collector.generate) and lists every component with a one-line description. It is the fastest way to find what already exists.

Wiring a binary

Binaries assemble their bundle list under cmd/<binary>/subcommands/run/. Example pattern:

return fxutil.Run(
    fx.Supply(...),
    core.Bundle(...),
    aggregator.Bundle(...),
    forwarder.Bundle(...),
    dogstatsd.Bundle(...),
    logsbundle.Bundle(...),
    // ...
    fx.Invoke(func(...components...) { /* start work */ }),
)

The Fx graph computes the topological order of OnStart calls, runs them, then waits for shutdown signals. On shutdown it invokes OnStop in reverse order.

Key bundles

Bundle Lives in What it provides
comp/core Bundle of fundamentals config, log, secrets, hostname, tagger, workloadmeta, autodiscovery, status, flare, healthprobe, IPC, gohai, agent telemetry
comp/aggregator comp/aggregator/ Demultiplexer
comp/collector comp/collector/ Collector (the check runtime)
comp/forwarder comp/forwarder/ Default forwarder, event-platform forwarder, orchestrator forwarder
comp/serializer comp/serializer/ Metrics and logs serializers
comp/dogstatsd comp/dogstatsd/ Server, listeners, mapper, replay
comp/logs comp/logs/ Logs agent, audit, integration support
comp/trace comp/trace/ Embedded trace agent
comp/metadata comp/metadata/ Inventory and host metadata payloads
comp/checks comp/checks/ Component-based checks (Windows event log, …)
comp/api comp/api/ HTTP API and gRPC server
comp/otelcol comp/otelcol/ OTel Collector integration
comp/snmptraps, comp/netflow, comp/networkpath, comp/ndmtmp Various Network device monitoring components
comp/haagent comp/haagent/ High-availability Agent logic
comp/updater comp/updater/ Agent self-update support
comp/process comp/process/ Process Agent core

Conventions

  • Interfaces in def/; one component per directory.
  • Fx options in fx/<name>fx/. Convention: a package named <name>fx so the import looks like import autoscalingfx "..." and the symbol is autoscalingfx.Module().
  • Mock implementations in mock/; tests import the mock instead of the impl.
  • Lifecycle hooks via compdef.Lifecycle — register OnStart/OnStop in the constructor.
  • Do not panic in constructors. Return errors instead so Fx can report a clean failure.
  • Avoid global variables; use Fx-injected dependencies. The migration is incremental, but new code is held to this standard.

Variants

For the same interface, multiple impls coexist:

  • impl/ — production.
  • mock/ — test mock.
  • Sometimes impl-none/ or impl-noop/ for no-op variants used in stripped flavors (serverless, IoT).
  • Sometimes fx-noop/ or fx-mock/ for the corresponding Fx wiring.

For example, comp/forwarder/defaultforwarder/ has full and no-op variants — the IoT Agent uses the no-op when it doesn't need to forward anything.

How a new component is added

  1. Generate the scaffold via .claude/skills/create-component/ (or by copying an existing minimal component).
  2. Define the interface in comp/<bundle>/<component>/def/component.go.
  3. Write the production implementation in impl/.
  4. Provide an fx/ package that constructs the impl and registers it with Fx.
  5. Write a mock/ package; add it to .mockery.yaml if the mock is generated.
  6. Add the new component to its bundle's bundle.go.
  7. Reference the component from the binaries that need it.
  8. Regenerate comp/README.md via dda inv collector.generate.

The .claude/skills/create-component/ skill walks through this in detail.

Migration patterns

When migrating from pkg/ to comp/:

  • Start with an interface that mirrors the existing public API of the pkg/ package.
  • Implement the component as a thin wrapper that calls into the existing pkg/ code.
  • Switch all callers to use the component (via Fx injection).
  • Move logic from pkg/ into impl/.
  • Delete the pkg/ package.

Most migrations stop at step 3 and 4 — they leave pkg/ packages alive as long as they're useful.

Key abstractions

Type / package Location Description
fx.Option go.uber.org/fx The unit of composition
fxutil pkg/util/fxutil/ Datadog's Fx helpers (test runners, supply helpers)
compdef.Lifecycle comp/def/ Lifecycle hook abstraction
bundle.Bundle() per-bundle The bundle's fx.Option

Entry points for modification

  • Adding a component: see "How a new component is added" above.
  • Adding a new bundle: create comp/<bundle>/bundle.go and reference it from the binaries that need it.
  • Changing component lifecycle: be very cautious — most components have Start / Stop semantics tested by other components. Update tests for both.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Components framework – Datadog Agent wiki | Factory