Datadog
Commercial SaaS observability platform. One vendor for metrics, logs, APM (traces), real-user monitoring, synthetic tests, security monitoring, and more. The “buy” choice when the alternative is running Prometheus + Grafana + Loki + Jaeger yourself.
For comparison with self-hosted stacks see 09_comparison_choosing.md. For Sentry / Grafana / Prometheus see siblings 01_sentry.md, 02_grafana.md, 03_prometheus.md.
What Datadog provides
| Product | What |
|---|---|
| Infrastructure metrics | hosts, containers, processes; built-in dashboards |
| APM (Application Performance Monitoring) | distributed traces, service maps |
| Logs | ingestion, search, alerting; pipelines for parsing/redacting |
| Real User Monitoring (RUM) | browser / mobile session replay, errors, performance |
| Synthetics | scripted external uptime/correctness checks |
| Security Monitoring | SIEM-like log + signal correlation |
| Database Monitoring | per-query stats from your DB |
| Network Monitoring | NPM (network performance monitoring) |
| Profiling | continuous profiler (Python via ddtrace) |
The pitch: one vendor, unified UI, correlated data (click from a metric spike to traces to logs to deploys), no infrastructure to run. Cost: per-host, per-million-spans, per-million-log-events. Real bills run into thousands per month for medium teams, six figures for large.
The Datadog Agent
Runs on each host (or as a DaemonSet in Kubernetes). Responsibilities:
- Collects host metrics (CPU, memory, disk, network).
- Receives metrics from apps via statsd-compatible “DogStatsD” protocol.
- Receives traces from apps via the trace agent.
- Tails log files and forwards to the SaaS.
- Auto-discovers services (Postgres, Redis, MySQL, etc.) and scrapes their metrics.
# Agent install (typical Docker)
docker run -d \
--name datadog-agent \
-e DD_API_KEY=<key> \
-e DD_SITE=datadoghq.com \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /proc/:/host/proc/:ro \
-v /sys/fs/cgroup/:/host/sys/fs/cgroup:ro \
gcr.io/datadoghq/agent:latest
The agent is the data plane; the SaaS is the storage + UI. No data leaves the host without going through the agent.
Custom metrics from Python (DogStatsD)
from datadog import statsd
# counters
statsd.increment("orders.placed", tags=["region:us-east", "tier:pro"])
# gauges
statsd.gauge("queue.depth", queue.size(), tags=["queue:emails"])
# histograms (distribution-aware)
statsd.histogram("request.duration", elapsed_ms, tags=["endpoint:/api/users"])
# distribution (better aggregation across hosts)
statsd.distribution("response.size", size_bytes)
statsd.distribution() is preferred over histogram for cross-host aggregation — histogram percentiles can’t be merged across hosts; distribution can.
Tags are key:value pairs. Datadog’s UI slices by them. Common tags: env, service, version, region, endpoint.
APM — distributed tracing
pip install ddtrace
Auto-instrumentation:
ddtrace-run python myapp.py
ddtrace-run monkey-patches popular libraries (Django, Flask, FastAPI, SQLAlchemy, Redis, requests, etc.). Spans are created automatically for HTTP requests, DB queries, cache calls. No code changes required for most apps.
Manual spans:
from ddtrace import tracer
@tracer.wrap(service="payments", resource="process_order")
def process_order(order):
with tracer.trace("payments.charge_card", resource=order.card.last4):
charge_card(order)
with tracer.trace("payments.send_receipt"):
send_receipt(order)
Set environment variables once:
export DD_SERVICE=my-app
export DD_ENV=production
export DD_VERSION=1.2.3
export DD_AGENT_HOST=localhost
These become tags on every span. Use them in the UI to scope dashboards and alerts.
Logs
Two ingestion paths:
- Tail files via agent: log file → agent → SaaS.
- Direct via the SDK:
datadog.loggeror any logger configured with a Datadog handler.
For Python:
import logging
import json_log_formatter
formatter = json_log_formatter.JSONFormatter()
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger = logging.getLogger("myapp")
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info("order processed", extra={
"order_id": order.id,
"user_id": user.id,
"amount": amount,
})
Structured JSON output goes to stdout. The Datadog agent parses it and ships to the SaaS, where you can query by field.
Trace correlation: if you also have ddtrace instrumented, the agent automatically injects dd.trace_id and dd.span_id into log records. Click a log line → see the originating trace.
Dashboards and alerts
Dashboards: drag-and-drop widgets querying metrics, logs, traces. Saved as JSON; can be versioned in git via Terraform or the Datadog API.
Alerts (“monitors”):
metric: avg:trace.web.request.duration{service:my-app}.as_count() > 5
window: last 5 minutes
notify: @slack-oncall, @pagerduty-critical
Monitor types: metric (threshold), anomaly (deviation from baseline), forecast (will it cross threshold?), composite (and/or of multiple), event-based, log-based.
For real alerting hygiene: write monitors in Terraform, review in PRs, avoid clicking around in the UI.
Pricing — the elephant in the room
Datadog bills by:
| What | Approximate cost (2024–25, list price) |
|---|---|
| Infrastructure host | $15–35/host/month |
| APM host | $31/host/month |
| Custom metrics | $0.05/metric/month |
| Logs ingestion | $0.10/GB |
| Logs retention | $1.27/million events/month (15-day retention) |
| RUM sessions | $1.50/1000 sessions |
| Synthetics | $5/10000 API tests |
Real-world examples:
- 100 hosts × $30 = $3000/month infrastructure alone.
- 10 TB logs ingestion = $1000/month — but if you retain, $$$ more.
- High-cardinality tags (e.g.
user_id:123on every metric) explode into hundreds of “custom metrics” each.
The bill grows non-linearly. Companies have horror stories of seven-figure annual Datadog spend.
Cost controls:
- Drop unused metrics via “metrics without limits.”
- Sample APM traces (default 1% in some configs; tune for cost vs coverage).
- Use exclusion filters to drop noisy logs (health checks, etc.).
- Set log indexing rules so verbose-but-rarely-searched logs don’t get indexed.
High-cardinality and the metrics bill
Datadog charges per unique metric+tags combination. So:
statsd.increment("api.requests", tags=[f"user_id:{user.id}"])
If you have 1M users, that’s 1M custom metrics. At $0.05 each, $50k/month for one counter.
Don’t put high-cardinality dimensions (user_id, session_id, trace_id, request_id) in metric tags. Put them in logs and traces, where you pay per-event not per-cardinality.
Tracing — sampling
Tracing every request becomes expensive at high RPS. Datadog’s APM samples:
- Head-based sampling: decision made at trace start. Simple but loses correlation across services.
- Tail-based sampling: decision after the trace completes. Keeps traces with errors / slow paths; drops uninteresting ones. Datadog supports both.
# Head sampling at 10%
from ddtrace import tracer
from ddtrace.sampler import RateSampler
tracer.sampler = RateSampler(sample_rate=0.1)
Or sample based on rules (always keep errors, always keep slow):
from ddtrace.sampling_rule import SamplingRule
tracer.sampler = DatadogSampler(rules=[
SamplingRule(sample_rate=1.0, service="payments"), # 100% of payments
SamplingRule(sample_rate=0.01, service="ads-tracking") # 1% of low-priority
])
Service Map
Datadog’s killer feature for distributed systems. Auto-generated graph of services calling services, based on trace data. Click a service → see its upstreams, downstreams, error rate, latency. Click a connection → see the slow / errored traces.
For microservices, this is the closest thing to “X-ray vision” into your architecture.
Datadog vs Prometheus + Grafana
The standard “buy vs build” trade-off:
| Datadog (SaaS) | Prometheus + Grafana (OSS) | |
|---|---|---|
| Setup time | minutes (install agent) | days–weeks (run Prometheus, Alertmanager, Grafana, long-term storage) |
| Storage | unlimited (SaaS handles) | self-managed; needs TSDB at scale (Thanos, Cortex, VictoriaMetrics) |
| Logs | bundled (Datadog Logs) | separate (Loki, Elasticsearch) |
| Traces | bundled (APM) | separate (Jaeger, Tempo) |
| Correlation | built-in (logs ↔ traces ↔ metrics) | manual via shared trace IDs |
| Cost | $$$ — scales with usage | infrastructure + people cost |
| Vendor lock-in | high | none |
| Custom dashboards | rich, JSON-exportable | Grafana JSON |
Pick Datadog when: you’d otherwise hire 2-3 SREs to run the OSS stack. Pick OSS when: cost matters, you have ops expertise, or you need vendor independence.
Hybrid: many teams emit OpenTelemetry to BOTH Datadog and a self-hosted stack, hedging vendor lock-in. See 07_opentelemetry.md.
Datadog gotchas
Agent missing data after deploy
If DD_AGENT_HOST isn’t set, ddtrace defaults to localhost:8126. In Docker without networking config, that doesn’t reach the agent. Symptoms: traces missing for the new service. Fix: DD_AGENT_HOST=host.docker.internal (Mac/Windows Docker) or proper Kubernetes service.
Metrics not appearing
Custom metrics may take 1-5 minutes to appear after first emission. Tags must be lowercase letters, digits, underscores, hyphens, periods, slashes — but Datadog silently normalizes some characters; build expected tag names and stick to them.
Log volume explosion
Verbose libraries (SQLAlchemy with echo=True, request loggers) push GB/day per host. Bill skyrockets. Audit log levels and add exclusion filters.
High-cardinality custom metrics
statsd.increment("api.requests", tags=[f"user_id:{user.id}"]) → unique metric per user. At scale = bankruptcy. Use logs/traces for high-cardinality.
Trace correlation breaks across queues
Default ddtrace propagates trace context via HTTP headers. Background jobs (Celery, RQ) need manual context injection — or use ddtrace’s queue integrations (which do it automatically for supported brokers).
Sampling decisions diverge
Head-based sampling: at request start, decide. If service A keeps a trace at 1% and service B keeps at 10%, only the union is in Datadog. Configure consistent rates or use tail-based sampling.
When Datadog is worth it
- Small team, lots to monitor: managed service saves SRE time.
- Multi-environment correlation needed: dev / staging / prod / multi-region in one UI.
- Compliance benefits: Datadog provides SOC 2 reports, helps with audit logging.
- Need APM with rich visualizations: service maps, flame graphs, breakdowns by tag.
- Logs + metrics + traces correlated: out-of-box; building this on OSS takes work.
When OSS wins
- Cost-sensitive at scale: $50k/year Datadog bill ≈ 1 senior SRE running OSS.
- Strict data residency: Datadog has regions but your data is still on their infrastructure.
- Need specific custom features: Prometheus + Grafana plugins are vast.
- Vendor lock-in concerns: your dashboards / alerts are in Datadog’s proprietary format.
Interview angle
- “What’s Datadog and why use it?” — SaaS observability platform unifying metrics, logs, APM (traces), RUM, synthetics. Buy-vs-build alternative to running Prometheus + Grafana + Loki + Jaeger. Pay for managed service + correlation between data types.
- “How does the Datadog Agent work?” — runs per-host (or k8s DaemonSet). Collects host metrics, receives app metrics via DogStatsD, traces via the trace agent, tails log files. Forwards everything to Datadog SaaS.
- “How would you instrument a Python service for Datadog APM?” —
pip install ddtrace, run withddtrace-run python app.pyfor auto-instrumentation of Django/Flask/FastAPI/SQLAlchemy/etc. SetDD_SERVICE,DD_ENV,DD_VERSIONenv vars. - “What’s the high-cardinality pitfall in Datadog metrics?” — Datadog bills per metric+tag-combo. Putting
user_idas a tag creates one metric per user → bill explodes. Keep high-cardinality dimensions in logs/traces, not metrics. - “Datadog vs Prometheus + Grafana — when each?” — Datadog when team is small + multiple data types + correlation matters + budget exists. OSS when cost matters, you have ops capacity, vendor independence is a requirement.
- “How do you control Datadog costs?” — drop unused custom metrics, sample APM traces (head or tail), exclude noisy logs (health checks, debug), control log indexing (ingest doesn’t equal index), tag discipline (no high-cardinality in metrics).
- “What’s tail-based vs head-based trace sampling?” — head-based decides at trace start (cheap, may miss errors). Tail-based decides after the trace completes (keeps interesting traces — errors, slow paths — drops boring ones). Datadog supports both.