Distributed Tracing and Logging System Design Template (Reference Design)
45-minute path, W3C trace context and baggage, async propagation, head, tail and hybrid sampling, span metrics, log correlation, retention tiers, failure modes
使用方法: Use it when designing or replacing a tracing and log pipeline for many services. Fill the volume assumptions and the sampling strategy first, then retention per data class and redaction. The main pitfall is head sampling alone, which keeps traces for only a small share of failed requests.
预览
System Design: [Tracing and logging platform name]
Reference design for a distributed tracing and log pipeline: how trace context crosses service and queue boundaries, how spans and logs are collected, which traces are kept (head, tail and hybrid sampling), how logs are correlated with traces, and how long each kind of data is retained. It uses W3C Trace Context (Recommendation, Level 1, 23 November 2021), W3C Baggage (Candidate Recommendation Snapshot, 30 May 2024) and OpenTelemetry (specification 1.62.0 as of October 2026, with the OTLP protocol and the Collector). It follows the structure of the System Design Document template, adds a 45-minute interview path, access-pattern and failure-mode tables and a cited "How real systems do it" box, and is pre-filled with a worked design to adapt. Replace every
[bracketed]value and every row marked Example; values marked Example are assumptions, not benchmarks.
Document control
| Field | Value |
|---|---|
| Document ID | [SDD-OBS-002] |
| System / feature | [Distributed tracing and centralised logging] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Implemented / Superseded] |
| Owner | [@owner] |
| Author(s) | [@author] |
| Reviewers | [@reviewer-1], [@reviewer-2] |
| Last updated | YYYY-MM-DD |
| Classification | [Internal] |
| Difficulty | Advanced |
| Specifications implemented | [W3C Trace Context Level 1; W3C Baggage (CR); OpenTelemetry specification 1.62.0; OTLP] |
| Related | [Observability specification], [Metrics platform design], [Data retention standard] |
Approvals
| Role | Name | Decision | Date |
|---|---|---|---|
| Tech lead | [@name] | [Approved / Changes requested] | YYYY-MM-DD |
| Staff / principal engineer | [@name] | [Decision] | YYYY-MM-DD |
| SRE / platform | [@name] | [Decision] | YYYY-MM-DD |
| Security and privacy | [@name] | [Decision] | YYYY-MM-DD |
| FinOps (telemetry budget) | [@name] | [Decision] | YYYY-MM-DD |
Revision history
| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft for design review |
| 0.2 | YYYY-MM-DD | [@name] | Example: Replaced 1% head sampling with hybrid head and tail sampling |
| 0.3 | YYYY-MM-DD | [@name] | Example: Added request metrics recorded in the SDK for every request, independent of sampling |
| 1.0 | YYYY-MM-DD | [@name] | Example: Approved |
Contents
1. Overview
1.1 Problem statement
[Example: About 2 000 services emit logs in four formats and three tracing libraries that do not share context. During last month's checkout outage it took 70 minutes to connect a customer's failed payment to the service that timed out, because trace IDs stopped at the message queue and logs carried no trace ID at all. The logging bill grew 60% in a year, mostly from debug logs nobody reads.]
1.2 Proposed solution
[Example: Every service uses the OpenTelemetry SDK and propagates W3C
traceparent, tracestate and baggage headers over HTTP, gRPC and
message headers. SDKs head-sample 25% of traces with a parent-based,
trace-ID-ratio sampler and attach the trace and span ID to every log record.
The SDK instrumentation also records request, error and duration metrics for
every request, sampled or not, and exports them as OTLP metrics. A Collector
agent on each node forwards the metrics and sends sampled spans with a
load-balancing exporter keyed by trace ID to a tail-sampling gateway tier.
The gateway keeps every error and slow trace plus 5% of the rest and writes
them to a trace store on object storage with a trace-ID index. Logs flow
through the same agents to Kafka, then to a hot search index for 7 days and
compressed object storage for 30 days.]
1.3 Decisions requested from reviewers
| ID | Question | Recommended option | Section |
|---|---|---|---|
| Q-01 | Example: Head, tail or hybrid sampling? | Hybrid: 25% head, then tail rules | 7.2 |
| Q-02 | Example: How do error rates stay exact if traces are sampled? | Request metrics recorded in the SDK for every request, independent of the sampling decision | 7.3 |
| Q-03 | Example: How is context carried through Kafka and batch jobs? | Trace headers in message headers; span links for fan-in | 7.1 |
| Q-04 | Example: Retention per data class? | Traces 14 days; logs 7 days hot, 30 days cold; audit logs 365 days | 7.5 |
| [Q-nn] | [Question] | [Option] | [Section] |
1.4 45-minute interview path
Use this order when the document serves an interview or a short design review. The written sections below are the long form of each step.
| Minutes | Cover | Output on the whiteboard |
|---|---|---|
| 0 to 5 | Scope: traces and logs, the incident questions they answer; below-the-line items (2.3) | Three queries: trace by ID, search traces, logs for a trace |
| 5 to 10 | Estimates: spans per second, bytes per day unsampled, tail buffer (section 3) | "About 0.41 PB a day unsampled, so we must sample" |
| 10 to 15 | Interface: traceparent header, OTLP export, query API (section 4) | Header format and two calls |
| 15 to 25 | High-level design: SDK, agent, gateway, stores, query (section 6) | One diagram, trace and log paths |
| 25 to 40 | One deep dive: sampling (default, 7.2 and 7.3), or context propagation across async boundaries (7.1), or log retention and cost (7.5) | Options, choice, reason |
| 40 to 45 | Failures (gateway overload, broken propagation), the 10x row in section 10 | Two failure rows |
Start the default deep dive with the naive answer, keep 1% of traces chosen at random at the first service, and show what it misses: 99% of failed requests have no trace. Then add tail sampling, show that it needs every span of a trace on one machine, and size the buffer that requires.
2. Requirements
2.1 Core functional requirements
| ID | Requirement | Priority |
|---|---|---|
| FR-01 | Example: Propagate trace context across HTTP, gRPC, Kafka and scheduled jobs | Must |
| FR-02 | Example: Retrieve a complete trace by trace ID | Must |
| FR-03 | Example: Search traces by service, operation, status, duration and selected attributes | Must |
| FR-04 | Example: Keep every trace that contains an error or exceeds the latency threshold of its entry operation, subject to head sampling (7.2) | Must |
| FR-05 | Example: Attach trace ID and span ID to every log record emitted inside a span | Must |
| FR-06 | Example: Search logs by service, severity, time and full text, and jump from a log line to its trace | Must |
| FR-07 | Example: Request rate, error rate and duration per service and operation computed from every request, not from sampled traces | Must |
| FR-08 | Example: Redact secrets and personal data before storage | Must |
| FR-09 | Example: Change sampling rules without redeploying services | Should |
| [FR-nn] | [Requirement] | [Must / Should / Could] |
2.2 Non-functional requirements
| ID | Quality | Target | Measured as |
|---|---|---|---|
| NFR-01 | Overhead on services | Example: Under 2% CPU and under 1 ms added p99 latency | Benchmarks with SDK on and off |
| NFR-02 | Ingest freshness | Example: Trace searchable within 60 s of its last span (p95); logs within 30 s | Synthetic canary traces |
| NFR-03 | Query latency | Example: Trace by ID under 2 s p95; log search over 1 h under 5 s p95 | Query logs |
| NFR-04 | Completeness | Example: At least 99% of stored traces have no missing spans | Orphan span rate |
| NFR-05 | Isolation | Example: Telemetry backpressure never blocks application requests | Chaos test with the gateway down |
| NFR-06 | Cost | Example: Telemetry spend at or below [x]% of infrastructure spend | Monthly FinOps report |
| [NFR-nn] | [Quality] | [Target] | [Measurement] |
2.3 Below the line (out of scope)
Name these explicitly so reviewers and interviewers do not pull the design toward them.
- [Example: the metrics platform itself (time series storage and alerting; see
system-design/design-metrics-monitoring)] - [Example: continuous profiling (the OpenTelemetry profiles signal is still in development as of October 2026)]
- [Example: real-user monitoring in browsers and mobile apps]
- [Example: security event management (SIEM) correlation rules]
- [Item deferred, with the release that will take it]
3. Capacity estimation
3.1 Assumptions
| Input | Symbol | Value | Source |
|---|---|---|---|
| Peak requests per second at the edge | Example: 400 000 | Edge metrics | |
| Average spans per trace | Example: 30 | Pilot services | |
| Bytes per span (OTLP protobuf, uncompressed) | Example: 800 B | Pilot export sizes | |
| Average to peak ratio | Example: 0.5 | Daily traffic curve | |
| Head sampling ratio | Example: 0.25 | 7.2 | |
| Tail decision wait | Example: 30 s | Longest common trace duration plus margin | |
| Share of traces with error or slow | Example: 3% | Pilot data | |
| Baseline keep ratio for normal traces | Example: 5% | 7.2 | |
| Peak log lines per second | Example: 2.5 million | Current log platform | |
| Bytes per log line | Example: 350 B | Current log platform |
3.2 Unsampled volume
At the average rate that is about 0.41 PB per day. Consequence: storing every span is not an option; sampling is the core of the design.
3.3 Tail-sampling buffer
The tail sampler must hold all spans of a trace until it decides. After 25% head sampling the gateway tier receives spans/s (2.4 GB/s):
Spread over 24 gateway instances that is 3 GB each, or 6 GB with 2x headroom. Without head sampling the same buffer would be 288 GB. Consequence: hybrid sampling keeps the tail tier at a modest, fixed size.
3.4 Stored traces
The tail sampler keeps all error and slow traces and 5% of the rest:
At the average rate that is 117 750 spans/s, 8.1 TB per day raw, about 1.0 TB per day after 8x columnar compression and about 14.2 TB for 14 days of retention. The trace-ID index holds about 339 million traces per day at 40 B each, 13.6 GB per day or 190 GB for 14 days. Consequence: the trace store fits on object storage with a small index, not a large search cluster.
3.5 Logs
| Step | Formula | Result |
|---|---|---|
| Peak ingest | 0.875 GB/s | |
| Raw per day (average) | 37.8 TB | |
| Compressed per day (10x) | 3.78 TB | |
| Hot index, 7 days, 1.5x index overhead | 39.7 TB | |
| Object storage, 30 days | 113.4 TB | |
| Audit class (10% of volume), 365 days | 138 TB |
Consequence: logs, not traces, dominate storage; the cheapest saving is dropping debug logs at the source and indexing only 7 days.
3.6 Summary
| Quantity | Estimate | Design consequence |
|---|---|---|
| Spans unsampled | 12 million/s, 9.6 GB/s at peak | Must sample |
| Tail buffer | 72 GB over 24 gateways | Hybrid sampling, trace-ID routing |
| Stored traces | About 2% of traces, 14.2 TB for 14 days | Object storage plus trace-ID index |
| Logs | 3.78 TB/day compressed | 7-day hot index, cold tier, source filtering |
| Export calls from SDKs | About 5 860/s at 512 spans per batch | Batching processor defaults are adequate |
4. API design
4.1 Interfaces
| Interface | Operation | Purpose |
|---|---|---|
| Propagation | traceparent, tracestate, baggage headers | Carry context between processes (7.1) |
| Ingest | OTLP over gRPC or HTTP to the local agent | Export spans, logs and metrics |
| Query API | GET /v1/traces/{trace_id} | Full trace for the waterfall view |
| Query API | GET /v1/traces?service=&operation=&status=error&min_duration_ms=&from=&to= | Trace search |
| Query API | GET /v1/logs?query=&service=&severity=&trace_id=&from=&to= | Log search and logs for one trace |
| Admin API | PUT /v1/sampling-policies/{id} | Change tail-sampling rules (versioned) |
4.2 Trace context on the wire
W3C Trace Context Level 1 defines traceparent as four dash-separated
fields: version (00), a 16-byte trace ID as 32 lowercase hex characters, an
8-byte parent span ID as 16 hex characters, and 8 bits of trace flags where
the lowest bit means sampled. tracestate carries vendor-specific data as a
list of at most 32 members. Trace Context Level 2 (Candidate Recommendation
Draft, 28 March 2024) adds a random-trace-ID flag (bit value 2); check its
status before relying on it.
GET /v1/orders/8812 HTTP/1.1
Host: orders.internal.example
traceparent: 00-7d3e1f9a5c2b4e8f9a0b1c2d3e4f5a6b-3c4d5e6f7a8b9c0d-01
tracestate: acme=q2v8n4kx
baggage: tenant.tier=gold,checkout.flow=express
Baggage carries application context (here a tenant tier) to downstream services; the W3C Baggage format sets minimum limits of 64 members and 8 192 bytes. Never put personal data or secrets in baggage: it is sent to every downstream service, including third parties.
4.3 Span as stored
The trace store keeps spans in the OTLP shape, flattened for columnar storage; times are Unix nanoseconds.
{"trace_id": "7d3e1f9a5c2b4e8f9a0b1c2d3e4f5a6b", "span_id": "9f8e7d6c5b4a3921", "parent_span_id": "3c4d5e6f7a8b9c0d", "name": "SELECT orders", "kind": "CLIENT", "start_unix_nano": 1793353447412000000, "end_unix_nano": 1793353447431000000, "status": "OK", "resource": {"service.name": "orders-api", "deployment.environment.name": "prod"}, "attributes": {"db.system.name": "postgresql"}, "links": []}
5. Data model
5.1 Entities
A trace is not stored as a row; it is the set of spans that share a trace ID. Log records point at spans by ID, and request metrics are series recorded for the same operations.
5.2 Storage choices
| Data | Store | Key | Main queries | Why |
|---|---|---|---|---|
| Spans | Example: Columnar blocks on object storage, one block per tenant per few minutes | Block sorted by trace_id | Trace by ID, search | Cheap, scales with object storage |
| Trace-ID index | Per-block Bloom filter plus a compact ID index | trace_id | Which blocks hold this trace | Avoids a large search cluster |
| Trace search index | Small columnar table of trace summaries | (root_service, start_time) | Search by service, status, duration | Searches touch summaries, not spans |
| Logs (hot) | Example: Log search cluster, 7 days | Index per day, sharded by service | Full-text and field search | Interactive search during incidents |
| Logs (cold) | Compressed files on object storage, 30 days; audit class 365 days | dt, service | Rare investigations, audits | Ten times cheaper per TB |
| Request metrics | Metrics platform | Series labels | Rates, errors, durations | Exact, cheap, alertable |
5.3 Access patterns
| Access pattern | Frequency | Key or index that serves it | Partition key rationale |
|---|---|---|---|
| Fetch full trace by ID | Every trace view, about 50/s | Trace-ID Bloom filters then block read | Blocks sorted by trace ID make one trace a contiguous read |
| Search traces by service and status | About 20/s | Trace summary table by (root_service, start_time) | Time-bounded scans over small rows |
| Logs for one trace | Every trace view | Log index field trace_id | Field index on the hot tier; cold tier reads only the trace's time range |
| Full-text log search | About 100/s | Inverted index per day | Daily indices drop cleanly at retention |
| Error rate per operation | Continuous (alerting) | Request metric series | Recorded for every request, independent of sampling, so exact |
| Route spans to tail sampler | Every sampled span (3 million/s) | Hash of trace_id in the load-balancing exporter | All spans of one trace reach the same gateway instance |
6. High-level design
Spans and logs share the node agent; they split into a trace path and a log path after it.
| Component | Responsibility | Scales by |
|---|---|---|
| OpenTelemetry SDK | Create spans, propagate context, record request metrics, head-sample, batch and export | With each service |
| Collector agent | Receive OTLP, add host metadata, forward metrics, route spans by trace ID | One per node |
| Tail-sampling gateway | Buffer spans per trace for 30 s, apply policies, forward kept traces | Instances, routed by trace ID |
| Redaction | Remove secrets and personal data by attribute rules | With gateway and log parsers |
| Trace store | Write columnar blocks, maintain trace-ID index, compact | Object storage and compactor count |
| Kafka logs topic | Buffer logs so a slow index never blocks agents | Partitions |
| Log search cluster | Index 7 days of logs | Shards per day |
| Query UI and API | Trace view, search, log-to-trace links | Stateless instances |
7. Detailed design
The deep dives in this section, each with options, choice and reason:
| Deep dive | Options considered | Choice | Reason | Section |
|---|---|---|---|---|
| Async propagation | Lose context at queues; carry headers in messages; span links | Headers in message metadata plus links for fan-in | Keeps one trace per request and honest batch traces | 7.1 |
| Sampling | Head only; tail only; hybrid | Hybrid: 25% head, tail rules | Bounded tail buffer, errors and slow traces kept | 7.2 |
| Exact service metrics | From stored traces; from spans in the agent; from SDK request metrics | SDK request metrics for every request | Spans dropped by head sampling never leave the SDK, so only metrics recorded in the process see every request | 7.3 |
| Retention | One retention for everything; per data class | Per data class and tier | Cost follows usefulness | 7.5 |
7.1 Context propagation
- Synchronous calls: SDK instrumentation for HTTP and gRPC injects and
extracts
traceparent,tracestateandbaggageon every call. Edge proxies keep incoming context only from trusted internal callers and start a new trace for public traffic, so outside clients cannot choose trace IDs or force sampling. - Messages: producers write the same fields into message headers (Kafka record headers, AMQP properties); consumers start a CONSUMER span whose parent is the producer span.
- Fan-in and batches: a job that processes 500 messages in one batch starts its own trace and adds a span link to each message's context, instead of choosing one parent arbitrarily.
- Threads and async code: use the SDK's context API to pass the active context into executors and callbacks; lost context is the main source of orphan spans (NFR-04).
7.2 Sampling
| Strategy | How it decides | Keeps errors? | Cost | Verdict |
|---|---|---|---|---|
| Head only | At the root, from the trace ID and a ratio | Only in proportion to the ratio | Lowest | Misses most failures |
| Tail only | After the trace completes, on all spans | All | Buffer of 288 GB and full span traffic to the gateway | Expensive at this volume |
| Hybrid (chosen) | 25% head at the root, then tail rules on the head-sampled traces | All errors among the 25% | 72 GB buffer | Balanced |
Head sampling uses ParentBased(root=TraceIdRatioBased(0.25)), so a child
service never re-decides and traces stay complete. The OpenTelemetry
specification marks TraceIdRatioBased stable but deprecated in favour of a
ProbabilitySampler that is still in development as of version 1.62.0; it
uses 56 bits of trace-ID randomness and records its threshold under the th
key of the ot entry in tracestate. Plan to migrate when that sampler is
stable in your SDKs.
Tail sampling needs every span of a trace on the same gateway instance. The agent uses the Collector's load-balancing exporter with the trace ID as the routing key, and the gateway runs the tail sampling processor with these policies:
| Policy ID | Rule | Keep |
|---|---|---|
| SMP-01 (Example) | Any span with status error | 100% |
| SMP-02 (Example) | Root span duration above the operation's latency threshold (from the SLO) | 100% |
| SMP-03 (Example) | Attribute tenant.tier=gold | 20% |
| SMP-04 (Example) | Everything else | 5% |
The consequence to state openly: only 25% of failed requests have a stored trace. Every failure still appears exactly in the request metrics (7.3) and in logs with its trace ID, which is usually enough to find a representative stored trace.
7.3 Request metrics independent of sampling
With parent-based head sampling in the SDK, a span that is not sampled is not recorded or exported, so the agent sees only 25% of traces and cannot count errors from spans. The SDK instrumentation therefore records request count, error count and a duration histogram per service, operation and status class for every request (the HTTP, RPC and messaging duration metrics of the OpenTelemetry semantic conventions), whatever the sampling decision, and exports them as OTLP metrics through the agent. If you later move head sampling from the SDK to the agent, a span-to-metrics step in the agent can do the same job, at the cost of exporting every span to the node. With about 2 000 services, 50 operations each, 3 status classes and 15 histogram buckets that is up to 4.5 million series. Cap operation names at the source (route templates, not raw URLs), or cardinality, not spans, becomes the cost problem.
7.4 Log pipeline and correlation
Services log through the OpenTelemetry logs bridge for their logging
library, so each record follows the stable OpenTelemetry log data model and
carries TraceId, SpanId and TraceFlags when emitted inside a span. The
agent batches logs to Kafka; parsers normalise severity to SeverityNumber,
redact (7.6) and write to the hot index and object storage in parallel.
Debug logs are off in production by default and can be enabled for one
service for 30 minutes through a dynamic configuration flag.
7.5 Retention
| Data class | Hot (searchable) | Cold | Deleted after | Reason |
|---|---|---|---|---|
| Traces | 14 days | None | 14 days | Incident and performance work happen within two weeks |
| Application logs | 7 days | 30 days on object storage | 30 days | Most searches cover the last 48 hours |
| Audit and security logs | 7 days | 365 days, write-once storage | 365 days or the period your policy sets | Investigations and audits |
| Request metrics | Per metrics platform policy | Downsampled | Per metrics platform policy | Long-term trends |
Retention for logs that can identify a person must follow your data retention standard. For example, the storage-limitation principle in EU GDPR and UK GDPR Article 5(1)(e) requires that identifiable data is kept no longer than needed. This is a design note, not legal advice; check the current official text and a qualified professional.
7.6 Redaction and access
Redaction runs twice: SDK attribute filters stop known sensitive attributes (authorization headers, cookies, card numbers) at the source, and the gateway and log parsers apply pattern rules as a second line. Access to traces and logs is by team and environment; production payload attributes are visible only to on-call roles, and every query is logged.
7.7 How real systems do it
How real systems do it (paraphrased, not copied). Google's technical report "Dapper, a Large-Scale Distributed Systems Tracing Infrastructure" (Sigelman and colleagues, dapper-2010-1, April 2010) describes tracing added to a small set of shared libraries so application developers did not have to instrument code by hand. Spans were written to local log files, collected by per-host daemons and stored with one wide-table row per trace and one column per span; median collection delay was under 15 seconds. The authors report that a uniform sampling rate as low as 1 in 1 024 still gave enough data for high-volume services, describe work towards adaptive sampling driven by a target number of traces, and add a second sampling stage in the collectors that hashes the trace ID so whole traces, not single spans, are kept or dropped. That trace-ID-based decision is the same idea behind
TraceIdRatioBasedand the trace-ID routing in 7.2. Read the original in 12.3.
8. Scalability and performance
| Concern | Approach | Limit before redesign |
|---|---|---|
| Span export from services | Batching processor (OpenTelemetry defaults: queue 2 048, batch 512, 5 s delay) | Queue drops show in SDK metrics; raise batch size before queue size |
| Tail buffer | 24 gateways, 6 GB each with headroom | Example: about 2x current traffic before adding instances |
| Gateway rebalancing | Consistent hashing on trace ID | Scaling the tier splits traces in flight for one decision window |
| Trace store writes | Columnar blocks, compaction | Object storage request rate per prefix |
| Log index | Daily indices, 7-day hot tier | Shard count per day |
Overhead budget per service request (NFR-01, under 1 ms p99):
| Step | Budget |
|---|---|
| Extract and inject context | 0.02 ms |
| Create and end spans (about 3 per service hop) | 0.05 ms |
| Log enrichment with trace ID | 0.01 ms |
| Export (asynchronous, off the request path) | 0 ms on the request path |
| Headroom | 0.92 ms |
9. Reliability, security and operations
9.1 Failure modes
| Component | Failure | Detection | Blast radius | Mitigation | RTO |
|---|---|---|---|---|---|
| SDK exporter | Agent unreachable | SDK dropped-span counter | Telemetry from that node only | Bounded queue, drop oldest; never block requests (NFR-05) | Example: recovers with the agent |
| Gateway instance | Crash | Load-balancer health checks | Traces in its 30 s buffer, about 1/24 of traces in flight | Exporter re-routes new traces; lost traces still appear in request metrics | Example: 1 min |
| Gateway tier | Overloaded | Memory and refused spans | All new traces | Shed by lowering the baseline policy (SMP-04) first, then head ratio via remote config | Example: 5 min |
| Propagation | A library strips headers | Orphan span rate per service | Broken traces through that service | Contract test per framework; alert on orphan rate above 1% | Example: 1 day (code fix) |
| Kafka logs topic | Broker loss | Under-replicated partitions | None with replication factor 3 | Leader election; agents buffer on disk | Example: 30 s |
| Log search cluster | Index slow or down | Ingest lag | Hot search only | Kafka buffers; cold tier still queryable | Example: 30 min |
| Redaction rules | Secret leaks into storage | Pattern scanner on stored samples | All data since the leak | Purge affected blocks, rotate the secret, add the rule | Example: 4 h |
| [Component] | [Failure] | [Signal] | [Scope] | [Mitigation] | [Time] |
9.2 Security and privacy
| Threat | Control |
|---|---|
| Outside client forces sampling or injects trace IDs | Edge restarts traces for untrusted callers (7.1) |
| Sensitive data in attributes, baggage or log bodies | Two-stage redaction; baggage never carries personal data |
| Over-broad access to production payloads | Role-based access by team and environment; query audit log |
| Telemetry as an exfiltration path | Egress from collectors only to approved endpoints over TLS |
| Tampering with audit logs | Write-once storage for the audit class; integrity checks |
| [Threat] | [Control] |
9.3 Observability of the pipeline
| Signal | Metric or log | Alert threshold |
|---|---|---|
| Dropped spans | SDK and Collector refused or dropped counters | Above 0.1% for 10 min |
| Tail buffer | Gateway memory and traces in flight | Above 70% of limit |
| Orphan spans | Spans whose parent never arrives, per service | Above 1% |
| Ingest freshness | Canary trace end-to-end delay | Above 60 s p95 |
| Log ingest lag | Kafka consumer lag | Above 120 s |
| Cost | Stored GB per day by team | Above budget by 20% |
9.4 Cost estimate
| Item | Unit cost | Quantity | Monthly cost |
|---|---|---|---|
| Collector agents | [$/vCPU-month] | Example: 0.25 vCPU per node | [$] |
| Tail-sampling gateways | [$/instance-month] | 24 | [$] |
| Trace object storage | [$/TB-month] | About 14.2 TB | [$] |
| Log search cluster | [$/node-month] | Example: sized for 39.7 TB | [$] |
| Log object storage | [$/TB-month] | About 113 TB plus 138 TB audit | [$] |
| Total | [$] |
10. Trade-offs and alternatives
| Decision | Options | Chosen | Why | What we give up |
|---|---|---|---|---|
| Instrumentation | Vendor agents; OpenTelemetry SDKs and Collector | OpenTelemetry | Open specification, one pipeline for all signals, vendor choice stays open | Some SDK features still in development |
| Sampling | Head; tail; hybrid | Hybrid | Errors kept, buffer bounded | 75% of failures have no stored trace |
| Trace storage | Search cluster; object storage with ID index | Object storage with ID index | Cost per TB, simple retention | Attribute search limited to summaries |
| Logs storage | All in search cluster; hot plus cold tiers | Hot 7 days, cold 30 days | Cost | Slower search beyond 7 days |
| Log transport | Agents write directly to index; via Kafka | Via Kafka | Index slowness never blocks agents | One more system to run |
| What changes at 10x / 0.1x | 10x (120 million spans/s): at 25% head sampling the tail buffer grows to 720 GB, so lower head sampling to 5% (144 GB), shard the gateway tier by tenant and move log indexing to per-team retention budgets. 0.1x (1.2 million spans/s): skip head sampling and tail-sample everything with a 28.8 GB buffer on two or three gateways, or use head sampling alone with a managed backend | Review at each doubling of spans per second | Tail buffer and log storage grow first | [Note] |
| [Decision] | [Options] | [Chosen] | [Why] | [Cost] |
11. Delivery plan and risks
11.1 Milestones
| Milestone | Scope | Exit criterion | Target date |
|---|---|---|---|
| M1 | Agents on all nodes, request metrics, head sampling for tier-1 services | Orphan rate below 1% on tier-1 paths | YYYY-MM-DD |
| M2 | Tail-sampling gateway and trace store | All error traces among head-sampled traces stored for 14 days | YYYY-MM-DD |
| M3 | Log pipeline via Kafka, trace ID on all logs | Log-to-trace link works for 95% of error logs | YYYY-MM-DD |
| M4 | Legacy tracing and log agents removed | Cost at or below budget for one month | YYYY-MM-DD |
11.2 Rollout and rollback
[Example: Roll out the SDK service by service behind a configuration flag, starting with the checkout path. The old log shipper runs in parallel for 30 days; rollback is turning the flag off, which stops export without a redeploy.]
11.3 Risks
| ID | Risk | Likelihood | Impact | Mitigation | Owner |
|---|---|---|---|---|---|
| R-01 | Example: Teams keep custom log formats without trace IDs | High | Medium | Logging library wrapper with the bridge built in; lint rule | [@name] |
| R-02 | Example: Attribute cardinality explodes request metrics | Medium | High | Allow-list of metric dimensions; route templates only | [@name] |
| R-03 | Example: Sampler API changes as the specification evolves | Medium | Low | Pin SDK versions, track the specification changelog | [@name] |
| R-04 | Example: Personal data stored before redaction rules are complete | Medium | High | Start with deny-by-default attribute filters | [@name] |
| [R-nn] | [Risk] | [L / M / H] | [L / M / H] | [Mitigation] | [@name] |
12. Appendices
12.1 Open questions
| ID | Question | Owner | Due |
|---|---|---|---|
| OQ-01 | Example: Do regulated tenants need traces kept longer than 14 days? | [@name] | YYYY-MM-DD |
| OQ-02 | Example: Adopt Trace Context Level 2 random flag once it is a Recommendation? | [@name] | YYYY-MM-DD |
| [OQ-nn] | [Question] | [@name] | YYYY-MM-DD |
12.2 Glossary
| Term | Meaning |
|---|---|
| Trace | All spans that share one trace ID, describing one request end to end |
| Span | One timed operation with a parent, attributes and status |
| Context propagation | Passing trace ID, parent span ID and flags across process boundaries |
| Baggage | Application key-value pairs propagated alongside trace context |
| Head sampling | Keep-or-drop decision at the start of a trace |
| Tail sampling | Keep-or-drop decision after the trace's spans have been seen |
| Span link | Reference from one span to another trace's span, used for batches |
| Orphan span | A span whose parent never arrives in storage |
| OTLP | The OpenTelemetry protocol for exporting telemetry |
| [Term] | [Meaning] |
12.3 References
| Reference | Use |
|---|---|
| W3C Trace Context, Recommendation (Level 1), 23 November 2021 | traceparent and tracestate in 4.2 and 7.1 |
| W3C Trace Context Level 2, Candidate Recommendation Draft, 28 March 2024 | Random trace-ID flag in 4.2 |
| W3C Propagation format for distributed context: Baggage, Candidate Recommendation Snapshot, 30 May 2024 | baggage header and limits in 4.2 |
| OpenTelemetry Specification 1.62.0, tracing SDK (samplers, batching processor) and logs data model | 7.2, 7.4, 8 |
| OpenTelemetry sampling concepts and Collector gateway deployment | Head and tail sampling, trace-ID routing in 7.2 |
| Sigelman B. H. et al., "Dapper, a Large-Scale Distributed Systems Tracing Infrastructure", Google Technical Report dapper-2010-1, April 2010 | 7.7 |
| ICO guide to UK GDPR: storage limitation | Retention note in 7.5 |
Observability Specification (ops/observability-spec) | Signals, SLOs and dashboards per service |
SLO Document (dev/slo-doc) | Latency thresholds used by SMP-02 |
Data Retention and Deletion Standard (data/data-retention-deletion-standard) | Retention periods in 7.5 |
Stream Processing Engine System Design (system-design/design-stream-processing-engine) | Log parsing and enrichment jobs |
Metrics and Monitoring System Design (system-design/design-metrics-monitoring) | Platform that stores the request metrics in 7.3 |
Back-of-Envelope Estimation and Capacity Model (system-design/back-of-envelope-estimation) | Method for section 3 |
Standard and licence note
Based on W3C Trace Context (Recommendation, 23 November 2021), W3C Baggage (Candidate Recommendation Snapshot, 30 May 2024) and the OpenTelemetry specification 1.62.0 as published in October 2026. Baggage and Trace Context Level 2 are not yet Recommendations, and several OpenTelemetry samplers are still in development, so check the current status pages before implementing. The Dapper report is paraphrased in 7.7 only; no text, figures or code were copied, and every number in this template is an illustrative example computed for this design. The retention note is not legal advice. Original text written for this collection. Template licence: MIT.
相关模板
Distributed Cache System Design Template
Distributed cache: cache-aside, delete on write, leases against herds, commit-log invalidation across regions, slot maps, hot keys, gutter pool, warm-up
系统设计Distributed File System Design Template (GFS and HDFS Style)
GFS/HDFS-style file system: metadata and chunk servers, chunk sizing, leases and write pipeline, record append consistency, re-replication, erasure coding
系统设计Distributed Key-Value Store System Design Template (Dynamo-Style)
Consistent hashing, vnodes, quorums (R+W>N), vector clocks vs LWW, hinted handoff, Merkle anti-entropy, gossip, LSM compaction, cache eviction, hot keys