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.