Our Pick OpenTelemetry — OpenTelemetry's vendor-neutral standard is the right long-term foundation for observability instrumentation. Datadog Agent wins for teams fully committed to Datadog who want zero-config setup and tighter integration with Datadog's analysis platform. The real-world answer for most teams: use OpenTelemetry for instrumentation, send data to Datadog (or any backend).
OpenTelemetry vs Datadog Agent

import ComparisonTable from ’../../components/ComparisonTable.astro’;

OpenTelemetry (OTel) and the Datadog Agent both collect traces, metrics, and logs from your applications and infrastructure. The fundamental difference: OpenTelemetry is a vendor-neutral open standard; Datadog Agent is a vendor-specific collection daemon tied to Datadog’s platform. The choice determines how portable your observability investment is.

Quick Verdict

Choose OpenTelemetry if: You want vendor flexibility, are using multiple backends, care about avoiding lock-in, or are building a greenfield observability stack.

Choose Datadog Agent if: You’re fully committed to Datadog’s platform, want the easiest setup for Datadog-specific features (APM, NPM, Live Processes), or need zero-config infrastructure monitoring.


Feature Comparison

<ComparisonTable headers={[“Feature”, “OpenTelemetry”, “Datadog Agent”]} rows={[ [“Vendor lock-in”, “None (CNCF standard)”, “Datadog-only”], [“Backend flexibility”, “Any (Datadog, Grafana, Jaeger, etc.)”, “Datadog only”], [“Traces”, “OTLP traces”, “Datadog APM”], [“Metrics”, “OTLP metrics”, “Datadog metrics”], [“Logs”, “OTLP logs”, “Datadog log management”], [“Auto-instrumentation”, “Yes (Java, Python, Node, Go, .NET)”, “Yes (similar languages)”], [“Infrastructure metrics”, “Limited (limited host metrics)”, “Comprehensive (CPU, memory, disk, network)”], [“Network performance”, “No native support”, “Datadog NPM (excellent)”], [“Live processes”, “No”, “Yes”], [“Container support”, “Manual config”, “Autodiscovery”], [“Setup complexity”, “Medium-High”, “Low (single agent install)”], [“Cost”, “Free (pay for backend)”, “Per host + per service”], [“Community”, “CNCF, large ecosystem”, “Datadog-owned”], ]} />


OpenTelemetry Architecture

OpenTelemetry separates instrumentation (in your code) from the backend (where data is stored and analyzed):

The OTel pipeline:

Application Code
  └── OTel SDK (auto or manual instrumentation)
        └── OTel Collector (receives, processes, exports)
              ├── Datadog backend
              ├── Grafana Tempo (traces)
              ├── Prometheus (metrics)
              ├── Loki (logs)
              └── Jaeger (traces)

Auto-instrumentation (Java example):

# Zero-code instrumentation — just add the agent
java -javaagent:opentelemetry-javaagent.jar \
     -Dotel.service.name=payment-service \
     -Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
     -Dotel.traces.exporter=otlp \
     -Dotel.metrics.exporter=otlp \
     -Dotel.logs.exporter=otlp \
     -jar payment-service.jar

# Automatically instruments:
# - HTTP client/server (Spring, Jersey, etc.)
# - Database queries (JDBC, MongoDB, Redis)
# - Messaging (Kafka, RabbitMQ)
# - AWS SDK calls
# No code changes required

Manual instrumentation (Node.js):

import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { trace, context, SpanStatusCode } from '@opentelemetry/api';

// Initialize SDK (do this before any imports)
const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'order-service',
    [SemanticResourceAttributes.SERVICE_VERSION]: '2.1.0',
    environment: process.env.NODE_ENV,
  }),
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector:4317',
  }),
});

sdk.start();

// Manual instrumentation
const tracer = trace.getTracer('order-service');

async function processOrder(orderId: string) {
  return tracer.startActiveSpan('processOrder', async (span) => {
    try {
      span.setAttribute('order.id', orderId);
      span.setAttribute('order.source', 'web');
      
      const order = await fetchOrder(orderId);
      span.setAttribute('order.value', order.totalValue);
      
      // Nested span for database operation
      const items = await tracer.startActiveSpan('fetchOrderItems', async (dbSpan) => {
        dbSpan.setAttribute('db.system', 'postgresql');
        dbSpan.setAttribute('db.operation', 'SELECT');
        try {
          return await db.query('SELECT * FROM order_items WHERE order_id = $1', [orderId]);
        } finally {
          dbSpan.end();
        }
      });
      
      await chargePayment(order);
      span.setStatus({ code: SpanStatusCode.OK });
      return { success: true, order };
    } catch (error) {
      span.setStatus({
        code: SpanStatusCode.ERROR,
        message: error.message,
      });
      span.recordException(error);
      throw error;
    } finally {
      span.end();
    }
  });
}

OTel Collector configuration:

# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    timeout: 5s
    send_batch_size: 1000
  
  memory_limiter:
    check_interval: 1s
    limit_mib: 512
  
  resource:
    attributes:
      - key: environment
        value: production
        action: insert

exporters:
  # Send to Datadog
  datadog:
    api:
      key: ${DATADOG_API_KEY}
      site: datadoghq.com
  
  # Also send to Grafana Tempo for traces
  otlp/tempo:
    endpoint: tempo:4317
    tls:
      insecure: true
  
  # Send metrics to Prometheus
  prometheus:
    endpoint: "0.0.0.0:8888"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch, resource]
      exporters: [datadog, otlp/tempo]
    
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [datadog, prometheus]
    
    logs:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [datadog]

Datadog Agent

The Datadog Agent is a single daemon that collects everything and sends it to Datadog:

Installation:

# Linux one-line install
DD_API_KEY=<your-api-key> DD_SITE="datadoghq.com" \
bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script_agent7.sh)"

# Docker
docker run -d --name datadog-agent \
  -e DD_API_KEY="<your-api-key>" \
  -e DD_SITE="datadoghq.com" \
  -e DD_LOGS_ENABLED=true \
  -e DD_APM_ENABLED=true \
  -e DD_PROCESS_AGENT_ENABLED=true \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v /proc/:/host/proc/:ro \
  -v /sys/fs/cgroup/:/host/sys/fs/cgroup:ro \
  -v /var/lib/docker/containers:/var/lib/docker/containers:ro \
  gcr.io/datadoghq/agent:7

# Kubernetes (DaemonSet via Helm)
helm install datadog-agent \
  -f datadog-values.yaml \
  --set datadog.apiKey="<your-api-key>" \
  --set datadog.site="datadoghq.com" \
  --set datadog.logs.enabled=true \
  --set datadog.apm.portEnabled=true \
  datadog/datadog

APM with Datadog:

# Python — ddtrace library
from ddtrace import tracer, patch_all
from ddtrace.contrib.flask import TraceMiddleware

# Auto-instrument common libraries
patch_all()

# Flask integration
app = Flask(__name__)
TraceMiddleware(app, tracer, service='web-server', distributed_tracing=True)

# Custom spans
with tracer.trace('database.query', service='db', resource='SELECT users') as span:
    span.set_tag('db.type', 'postgresql')
    span.set_tag('db.instance', 'prod-db-1')
    result = db.execute(query)

Datadog Autodiscovery (containers):

# Docker label-based autodiscovery
# No config file needed — labels in docker-compose.yml
services:
  redis:
    image: redis:7
    labels:
      com.datadoghq.ad.check_names: '["redisdb"]'
      com.datadoghq.ad.init_configs: '[{}]'
      com.datadoghq.ad.instances: '[{"host":"%%host%%","port":"6379"}]'

  postgres:
    image: postgres:15
    labels:
      com.datadoghq.ad.check_names: '["postgres"]'
      com.datadoghq.ad.init_configs: '[{}]'
      com.datadoghq.ad.instances: >
        [{"host":"%%host%%","port":"5432",
          "username":"datadog","password":"%%env_POSTGRES_PASSWORD%%"}]

The Hybrid Approach (Best of Both)

Most mature teams use OpenTelemetry for instrumentation and Datadog as the backend:

Application → OTel SDK (auto-instrumentation)
            → OTel Collector
                  └── Datadog exporter → Datadog (traces, metrics, logs)

Benefits:
- Datadog's excellent analysis UI and features
- No vendor lock-in in your code (swap backend without code changes)
- OTLP is the industry standard — other teams can use your instrumentation
- Datadog officially supports OTel ingestion

Datadog Agent still used for:
- Host metrics (CPU, memory, disk, network)
- Network Performance Monitoring
- Live Processes monitoring
- Infrastructure logs (not application logs)

Cost Comparison

OpenTelemetry itself: Free (open source)
But you pay for the backend:

Datadog pricing (approximate):
- APM: $31/host/month + $1.70/million spans
- Infrastructure: $15-23/host/month
- Log Management: $0.10/GB ingested + $1.70/million indexed
- Total for 20 services: $800-2,000+/month

Grafana Cloud (OTel-native backend):
- Free tier: 50GB metrics + 50GB logs + 50GB traces
- Pay as you grow: $8/1000 series (metrics)
- Much cheaper at moderate scale

Self-hosted (Prometheus + Jaeger + Loki):
- Infrastructure cost only: $200-500/month
- High operational burden (you maintain the stack)

When to Choose Each

Choose OpenTelemetry:

  • Greenfield observability — start with the standard
  • Multi-cloud or multi-backend requirements
  • Strong vendor flexibility preference
  • Building internal platform teams want others to instrument
  • When you might change observability backends in 2-3 years

Choose Datadog Agent (alongside or instead):

  • Fully committed to Datadog for 3+ years
  • Need Datadog-specific features: NPM, Live Processes, Watchdog
  • Want the easiest path for infrastructure metrics
  • Team prefers vendor support over open source

Bottom Line

OpenTelemetry has won the instrumentation standard debate — it’s the CNCF-backed, vendor-neutral approach that all major observability platforms now support. The right architecture for most teams: instrument with OTel SDKs (no vendor code in your application), route through the OTel Collector, and export to Datadog for analysis. Run the Datadog Agent alongside for host metrics and infrastructure monitoring. This gives you Datadog’s excellent analysis capabilities without locking your instrumentation to a single vendor.