Home  /  DevOps  /  CDN Observability Stack
DevOps

CDN Observability Stack: Logs, Metrics, and Traces for Edge Infrastructure

Sarah Mitchell, Edge Computing Engineer September 9, 2026 9 min read

Your CDN serves 80% of your traffic, but you only observe the origin. When cache hit ratio drops, TTFB spikes in Southeast Asia, or an edge function silently fails, traditional monitoring leaves you blind. A proper observability stack brings logs, metrics, and traces together at the edge — so you can debug what happened, where, and why in seconds.

Edge infrastructure is fundamentally different from centralized cloud workloads. Requests terminate in 300+ Points of Presence, logic executes in isolates or containers within 50ms of the user, and state is ephemeral. If your observability pipeline was designed for origin servers, it will miss the most critical hop. This guide shows how to build a production-grade CDN observability stack that captures high-cardinality edge signals without drowning in cost or cardinality.

Why Edge Observability Fails With Legacy Monitoring

Most teams start with CDN-provided dashboards and origin APM. That covers availability, but not explainability. A spike in cache_status=MISS in Singapore could be a cache invalidation bug, an origin latency regression, a TLS negotiation failure, or a bot flood bypassing cache. Without correlated logs and traces, you are guessing.

Three edge-specific challenges break traditional stacks:

The solution is not more dashboards. It is a unified pipeline that treats logs, metrics, and traces as complementary signals, sampled and aggregated at the edge before centralization. If you are still relying on basic uptime checks, read our primer on CDN monitoring and observability fundamentals before implementing this stack.

The Three Pillars at the Edge

Observability is often described as three pillars, but at the CDN layer each pillar answers a different operational question. You need all three, but you will use them in different workflows.

Performance Comparison: Edge vs Origin Processing
Metric Edge Processing Origin Processing Improvement
Latency (p50)8–15 ms120–300 ms15–20x faster
Cache Hit Ratio85–95%N/AReduces origin load
TLS Handshake1–5 ms (nearby PoP)50–150 ms10–30x faster
Bandwidth Cost$0.01–0.04/GB$0.05–0.12/GB3–5x cheaper
Availability99.99% (multi-PoP)99.9% (single DC)10x fewer outages

1. Logs: The Ground Truth for Every Request

Logs are your discrete, high-fidelity record of what happened. For CDNs, that means structured access logs enriched with edge context: pop, cache_status (HIT, MISS, STALE, BYPASS), edge_compute_duration, origin_ttfb, tls_version, http_version, client_asn, and waf_action.

Do not ship unstructured Nginx-style logs. Emit JSON from the edge and filter aggressively before egress. Drop health checks, favicon requests, and known bot traffic at the collector. Keep hot logs for 7-14 days for debugging, then archive to object storage in Parquet for audit.

Effective log pipelines use Vector or Fluent Bit deployed as close to the PoP as possible — often via log push from your CDN provider to your collector. Enrich logs with GeoIP and ASN at ingest, not query time, to keep queries fast. For teams running logic at the edge, correlate logs with edge computing workloads by injecting request_id and function_version into every log line.

Pro tip: Add cache_key and surrogate_key to your log schema on day one. When you later debug cache invalidation issues, you will be able to pinpoint which purge tag or key pattern caused a MISS storm without replaying traffic.

2. Metrics: Your Early Warning System

Metrics are aggregated, numeric signals optimized for alerting and trend detection. At the edge, the golden signals are: request rate, error rate (4xx/5xx by PoP), latency (p50/p95/p99 TTFB by region), cache hit ratio, origin offload, and edge compute duration.

Collect metrics via Prometheus exposition or OTel metrics, but control cardinality ruthlessly. Never use url or cache_key as a label. Instead, aggregate by pop, cache_status, status_code_class, country, and function_name. Use histograms for TTFB with buckets tuned for edge latencies: [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2] seconds.

Store metrics in Prometheus with Thanos/Cortex or managed Grafana Mimir for long-term retention. Derive key SLOs from these metrics: cache hit ratio > 92%, p95 edge TTFB < 120ms, and 5xx < 0.1% per PoP. Unlike logs, metrics should be retained for 13 months to support capacity planning and seasonal comparison, especially for ecommerce traffic peaks.

3. Traces: Connecting Edge to Origin

Traces show the causal chain for a single request across edge and origin. A trace for a cache MISS might contain spans for: edge.request → edge.worker.auth → kv.get → origin.fetch → image.optimize. With W3C Trace Context propagation, you can follow a request from Cloudflare Workers, Fastly Compute, or AWS Lambda@Edge all the way to your API.

Traces are essential for debugging tail latency. If p99 latency spikes only for HTTP/3 clients in Brazil, a trace will reveal whether the bottleneck is QUIC handshake, edge KV, or origin. For modern protocols, correlate trace data with your HTTP/2 and HTTP/3 performance to isolate transport-layer issues.

Sample intelligently. Head-based sampling at 1% is cheap but misses rare errors. Implement tail-based sampling in the OpenTelemetry Collector: keep 100% of traces with errors, cache MISS with origin latency > 500ms, or WAF blocks, and 1% of successful HITs. This keeps trace volume under 5% of log volume while preserving debuggability.

Reference Architecture: From PoP to Grafana

A resilient stack separates collection, processing, and storage. Edge PoPs push telemetry to a regional collector tier, which does filtering, sampling, and enrichment before forwarding to centralized storage. This avoids backpressure at the edge and keeps egress costs predictable.

CDN Observability Pipeline Flow from clients through edge PoPs to collectors and storage backends CLIENTS Browser / App / Bot W3C Trace Headers EDGE POPs 300+ PoPs Cache + Workers WAF / TLS / QUIC OTel SDK / Log Push COLLECTOR OTel Collector / Vector Filter & Sample Enrich GeoIP/ASN Route by Signal Loki / ClickHouseLogs (7-14d hot) Mimir / PrometheusMetrics (13 months) Tempo / JaegerTraces (30d sampled) GRAFANA Correlated Views SLOs & Alerts
Figure 1: Edge-native observability pipeline — telemetry is filtered and sampled at the collector tier before centralized storage to control cost and cardinality.
Key Design Principle: Push, Don't Pull

Do not scrape edge PoPs with Prometheus. Edge nodes are ephemeral and often behind NAT. Use log push, OTLP export, and Prometheus remote-write from the collector tier. This also ensures you capture data even when a PoP is unhealthy and cannot be scraped.

Choosing Your Storage: Signals vs. Cost

Not all telemetry belongs in the same database. The table below maps each signal to its ideal storage and query pattern.

Signal Question It Answers Storage Retention & Sampling
Logs
Structured Access Logs
Why did this specific request MISS? Which WAF rule fired? Loki, ClickHouse, or S3 + Athena 7–14 days hot, 90 days archive; filter >50% at edge
Metrics
Aggregated Counters & Histograms
Is cache hit ratio degrading in EU? Is p95 TTFB breaching SLO? Prometheus + Mimir / Thanos 13 months; aggregate by PoP, status class, region
Traces
Distributed Spans
Where did latency accumulate for this cache MISS chain? Tempo, Jaeger, Honeycomb 30 days; tail-sample 1–5%, keep 100% errors
Events
Deploys & Purges
Did a new worker version or purge cause the spike? Loki annotations / Grafana 90 days; correlate with metric anomalies

Implementation: Code and Configuration

Start small: instrument one edge function and one PoP group, then expand. Below are production-tested snippets for the collector and edge.

Vector + OpenTelemetry Collector for Log Enrichment

Use Vector to receive CDN log push (e.g., Cloudflare Logpush or Fastly logging endpoint), parse JSON, enrich, and forward. This example drops health checks and derives a metric for cache hit ratio.

vector.toml — edge log pipeline[sources.cdn_logs]
type = "http_server"
address = "0.0.0.0:8080"
encoding = "json"

[transforms.filter]
type = "filter"
inputs = ["cdn_logs"]
condition = '.path != "/healthz" && .user_agent != "ELB-HealthChecker"'

[transforms.enrich]
type = "remap"
inputs = ["filter"]
source = '''
  .pop = get_env_var("POP") ?? .pop
  .cache_status = upcase(.cache_status)
  # Derive metric-friendly fields
  .status_class = to_string(to_int(.status) / 100) + "xx"
'''

[sinks.loki]
type = "loki"
inputs = ["enrich"]
endpoint = "http://loki:3100"
labels = { pop="{{pop}}", cache_status="{{cache_status}}", status_class="{{status_class}}" }

[sinks.prometheus]
type = "prometheus_remote_write"
inputs = ["enrich"]
endpoint = "http://mimir:9009/api/v1/push"

Edge Worker Tracing with OpenTelemetry

For Cloudflare Workers, Fastly Compute, or Lambda@Edge, propagate W3C trace context and create spans for KV and origin calls. Keep span attributes low-cardinality.

worker.js — OTel tracing at the edge (simplified)import { trace, context } from '@opentelemetry/api';

export default {
  async fetch(request, env) {
    const tracer = trace.getTracer('edge-worker');
    return tracer.startActiveSpan('edge.request', async (span) => {
      span.setAttribute('http.method', request.method);
      span.setAttribute('cdn.pop', env.POP);
      span.setAttribute('cdn.cache_status', 'MISS'); // set after cache lookup

      // Trace KV lookup separately
      const kvSpan = tracer.startSpan('kv.get', {}, context.active());
      const data = await env.CACHE_KV.get(request.url);
      kvSpan.end();

      if (!data) {
        const originSpan = tracer.startSpan('origin.fetch');
        const res = await fetch(request);
        originSpan.setAttribute('origin.ttfb_ms', res.headers.get('x-origin-ttfb'));
        originSpan.end();
        span.setAttribute('cdn.cache_status', 'MISS');
        span.end();
        return res;
      }
      span.setAttribute('cdn.cache_status', 'HIT');
      span.end();
      return new Response(data);
    });
  }
}

Prometheus Alerting for SLOs

Define burn-rate alerts on metrics, not logs. This alert fires when cache hit ratio drops below SLO for 10 minutes.

alerts.yml — SLO burn rategroups:
- name: cdn.slo
  rules:
  - alert: CacheHitRatioLow
    expr: |
      sum(rate(cdn_requests_total{cache_status="HIT"}[5m]))
      /
      sum(rate(cdn_requests_total[5m])) < 0.92
    for: 10m
    labels: { severity: warning }
    annotations:
      summary: "Cache hit ratio {{ $value | humanizePercentage }} below 92% in {{ $labels.pop }}"
      runbook: "Check recent purges and origin latency"

Secure the entire pipeline with mTLS between edge and collector, and ensure your SSL/TLS configuration does not strip trace headers. Many WAFs drop traceparent by default — allowlist it.

From Data to Action: SLOs, Dashboards, and Runbooks

Observability without action is overhead. Build three Grafana dashboards:

  1. Edge Overview: Global hit ratio, p50/p95 TTFB by continent, 5xx by PoP, and WAF block rate. Use for NOC triage.
  2. Cache Debugging: Log panel filtered by cache_status and surrogate_key, correlated with purge events. Use for invalidation and origin debugging.
  3. Edge Compute: Function duration histogram, cold start rate, KV hit ratio, and trace waterfall for slow requests.

Link logs to traces via trace_id — Grafana Loki and Tempo do this natively with derived fields. When an alert fires, the on-call engineer should click from metric spike → exemplar trace → full logs for that request_id in under 30 seconds.

Finally, test your stack with chaos. Purge a high-traffic surrogate key, deploy a canary worker that adds 50ms latency, and verify that sampling, alerting, and correlation work before a real incident.

Adopting this stack incrementally is more sustainable than a big bang migration. Start with structured logs and hit-ratio metrics this sprint, add tail-based tracing next sprint, and layer SLOs once data is reliable. The payoff is immediate: faster MTTR, fewer false positives, and the ability to confidently ship edge logic knowing you can observe its impact globally.

Frequently Asked Questions

What is the difference between CDN monitoring and CDN observability?
Monitoring tells you when something is wrong using predefined dashboards and alerts. Observability lets you ask arbitrary questions about edge behavior by correlating high-cardinality logs, metrics, and traces without redeploying. For CDNs, monitoring might alert on 5xx rate, while observability lets you trace a single cache MISS through edge logic, origin fetch, and TLS handshake.
How long should I retain CDN logs at the edge?
Keep hot, high-cardinality logs for 7–14 days in Loki or ClickHouse for debugging, downsampled metrics for 13 months for capacity planning, and traces sampled at 1–5% for 30 days. For compliance or billing, archive raw access logs to S3/GCS with 90–365 day retention using Parquet and lifecycle policies.
Do I need OpenTelemetry for CDN observability?
Not strictly, but OpenTelemetry is the fastest path to unified traces and metrics from edge workers, load balancers, and origins. Most modern CDNs and edge runtimes now export OTel natively. If your CDN only supports log push, use Vector or Fluent Bit to convert logs to OTel metrics and traces at the collector layer.
How do I reduce observability costs for high-traffic CDNs?
Sample aggressively at the edge, filter health checks and bots before ingestion, use tail-based sampling for traces, aggregate metrics with Prometheus recording rules, and send only structured JSON logs with required fields. Moving from full log ingestion to derived metrics can cut costs by 60–80% without losing alerting fidelity.

References & Standards: W3C Web Performance · RFC 7234