Logging System
Design a high-throughput, searchable logging pipeline that ingests terabytes of logs per day.
Problem Statement
Design a centralized Logging System like Elasticsearch, Logstash, Kibana, OpenSearch, or Grafana Loki. Applications and infrastructure running across many hosts emit structured logs, local agents collect them, and the platform must ingest, retain, search, and analyze those logs for debugging, auditing, and incident response.
At interview scale, assume hundreds of thousands of hosts, millions of log events per second, bursty traffic during incidents, and hundreds of terabytes to petabytes of logs per day. The hard part is not accepting a single log line; it is decoupling producers from indexing, applying backpressure without losing critical data, choosing what to index, controlling storage cost, enforcing tenant isolation, and making recent logs searchable fast enough for live debugging.
The default design should optimize for durable high-throughput ingestion, predictable query latency over recent data, and cost-aware retention. It should also make explicit tradeoffs between Elasticsearch-style full-text indexing and Loki-style label indexing with compressed chunks in object storage.
Business use case
Logs are the forensic record of a production system. Engineers use them to debug incidents, security teams use them for audits and investigations, and operators use them to understand deployments, customer impact, and error patterns.
A strong logging platform reduces mean time to detect and recover by letting teams search recent events quickly, correlate logs with traces and metrics, and preserve older logs for compliance at lower cost. It is also a shared infrastructure service, so multi-tenancy, quota enforcement, and noisy-neighbor protection are product requirements, not afterthoughts.
Functional Requirements
Collect logs from hosts, containers, serverless workloads, and managed services through agents or direct APIs.
Accept structured JSON logs and support parsing of common unstructured text formats.
Durably buffer incoming logs so indexing outages or traffic spikes do not immediately drop data.
Enrich events with tenant, service, environment, host, region, trace_id, deployment version, and severity metadata.
Index recent logs for search by time range, labels, fields, and free text depending on the chosen backend.
Support tailing live logs, historical search, aggregation by fields, and export for investigations.
Apply per-tenant retention policies across hot, warm, and cold storage tiers.
Enforce tenant access control, quotas, sampling, and rate limits under log storms.
Non-Functional Requirements
Ingestion throughput
The system must sustain millions of events per second and tolerate 5x to 10x bursts during incidents. Ingestion should be horizontally scalable by tenant, topic, partition, and index shard.
Durability
Accepted critical logs should survive gateway, worker, and index cluster failures. Acknowledgement should happen only after a durable append to Kafka or an equivalent replicated buffer, while lower-priority debug logs may be sampled or dropped under explicit policy.
Query latency
Recent debugging queries over a bounded time range should return first results in under 2 seconds p95 and complete common searches in under 10 seconds. Very broad historical searches should run as asynchronous jobs with progress and partial results.
Availability
Ingestion availability is more important than interactive search during incidents. The design should continue accepting logs when indexing is degraded, and search should degrade by tier or time range rather than failing globally.
Cost efficiency
Full-text indexing every field forever is too expensive at petabyte scale. Use hot indexes for recent data, warm lower-cost nodes or reduced replicas for older searchable data, and compressed object storage for long-term cold retention.
Multi-tenancy isolation
One noisy tenant or runaway service must not consume all ingestion, index, or query capacity. Enforce per-tenant quotas, separate high-value tenants where needed, isolate Kafka partitions and index namespaces, and budget query fan-out.
Freshness and ordering
Logs should usually be searchable within seconds of being emitted. Global ordering is not required, but per-stream ordering should be preserved where practical, and late or out-of-order events must still be indexed into the correct time bucket.
Capacity Estimation
Assumptions
Assume 200,000 hosts across production, staging, and shared infrastructure. Each host emits 10 log events per second on average, with a 5x incident burst. Average event size is 1 KB before compression, including timestamp, service, severity, message, labels, trace_id, and request context.
Assume one Kafka replica factor of 3, four-to-one compression for long-term object storage, one hot full-text searchable tier for 7 days, one warm searchable tier for 23 additional days, and one cold object-storage tier for 365 days. Hot full-text indexing is estimated at 1.5x raw event size for primary index structures and one replica for availability.
Average event rate
2M events per second
200,000 hosts times 10 events per second
Peak event rate
10M events per second
5x burst during incidents or bad deployments
Events per day
172.8B events
2M events per second times 86,400 seconds
Raw ingest volume
173 TB per day
172.8B events times 1 KB each
Peak ingest bandwidth
10 GB per second raw
10M events per second times 1 KB
Kafka retained buffer
1.6 PB for 72 hours
173 TB per day times 3 days times 3 replicas before compression savings
Hot searchable storage
3.6 PB for 7 days
173 TB per day times 1.5 index expansion times 2 copies times 7 days
Cold object storage
15.8 PB for 365 days
173 TB per day divided by 4 compression times 365 days
Kafka partitions
400 to 600 partitions
Enough headroom for 10 GB per second peak at about 20 to 30 MB per second per partition
Index shards per hot day
1,000 to 2,000 primary shards
Keeps shard sizes near 100 to 200 GB before replicas and allows tenant isolation
Calculations
- Event rate: 200,000 hosts times 10 events per second is 2,000,000 events per second average.
- Daily events: 2,000,000 events per second times 86,400 seconds is 172,800,000,000 events per day.
- Raw volume: 172.8B events times 1 KB is about 172.8 TB per day, rounded to 173 TB per day.
- Peak bandwidth: 10M events per second during a 5x burst times 1 KB is 10 GB per second before protocol overhead.
- Kafka buffer: keeping 72 hours of raw data at 173 TB per day is 519 TB. With three replicas, reserve roughly 1.6 PB before compression and operational headroom.
- Hot index: full-text indexing can cost about 1.5 times raw size for primary data. One replica doubles it, so 173 TB times 1.5 times 2 is about 519 TB per hot day. Seven days is about 3.6 PB.
- Cold storage: object storage with four-to-one compression stores 173 TB divided by 4, or about 43.25 TB per day. One year is about 15.8 PB.
- Partitions: if a Kafka partition safely handles about 20 to 30 MB per second sustained in this workload, 10 GB per second peak needs about 334 to 500 partitions. Choose 400 to 600 plus tenant-aware partitioning.
- Shards: if one hot primary shard should stay near 100 to 200 GB, a 173 TB raw day expanded to about 260 TB primary index data needs roughly 1,300 to 2,600 shards. With tenant and tier optimization, plan around 1,000 to 2,000 primary shards per hot day and adjust based on measured shard size.
API Design
/api/v1/logs/batchIngests a compressed batch of logs from an agent, collector, or service. The gateway authenticates the tenant, validates the batch envelope, applies quotas, and appends accepted records to the durable buffer.
Request
{
"tenantId": "tenant_123",
"source": "checkout-api",
"format": "json",
"compression": "gzip",
"events": [
{
"timestamp": "2026-07-26T06:59:19Z",
"level": "ERROR",
"message": "payment authorization failed",
"trace_id": "trc_abc123",
"region": "us-east-1"
}
]
}
Response
{
"accepted": 10000,
"rejected": 3,
"bufferOffset": "logs-prod-128:88420201",
"retryAfterMs": 0
}
202— Batch durably accepted for processing400— Invalid batch, schema, timestamp, or compression401— Authentication failed413— Batch exceeds size limit429— Tenant or source rate limit exceeded503— Ingestion temporarily unavailable
/api/v1/searchRuns a bounded log search over recent or historical data. Small recent searches may complete synchronously; expensive searches return a job identifier and stream partial results.
Request
{
"tenantId": "tenant_123",
"query": "level:ERROR service:checkout-api authorization",
"startTime": "2026-07-26T06:00:00Z",
"endTime": "2026-07-26T07:00:00Z",
"limit": 200,
"mode": "auto"
}
Response
{
"mode": "sync",
"results": [
{
"timestamp": "2026-07-26T06:59:19Z",
"service": "checkout-api",
"level": "ERROR",
"message": "payment authorization failed"
}
],
"nextCursor": "cursor_001",
"scannedBytes": 734003200
}
200— Search completed synchronously202— Search job accepted for asynchronous execution400— Invalid query or time range403— Caller lacks access to tenant or fields429— Query budget exceeded
/api/v1/search/jobs/{jobId}Returns progress and partial results for a long-running historical query. The query service uses this endpoint when a search spans cold storage, many shards, or a large time window.
Response
{
"jobId": "job_789",
"status": "running",
"progressPercent": 64,
"partialResultCount": 10000,
"scannedBytes": 3298534883328,
"expiresAt": "2026-07-26T08:00:00Z"
}
200— Job status returned403— Caller cannot access this job404— Job not found or expired
/api/v1/tailStreams near-real-time logs for a tenant, service, or label selector. This is optimized for debugging active incidents and should have stricter time and result limits than historical search.
Request
tenantId=tenant_123&selector=service:checkout-api level:ERROR&follow=true
Response
event: log
data: {"timestamp":"2026-07-26T06:59:21Z","level":"ERROR","message":"retry budget exhausted"}
200— Stream opened400— Invalid selector403— Caller lacks access429— Too many active streams
/api/v1/tenants/{tenantId}/retention-policyCreates or updates retention, indexing, sampling, and storage-tier policies for a tenant. Policy changes apply to new data immediately and to older data through lifecycle jobs.
Request
{
"hotDays": 7,
"warmDays": 30,
"coldDays": 365,
"indexMode": "full_text_for_errors_label_index_for_debug",
"samplingRules": [
{ "level": "DEBUG", "sampleRate": 0.1 }
]
}
Response
{
"tenantId": "tenant_123",
"version": 42,
"status": "active"
}
200— Policy updated400— Invalid retention or sampling rule403— Caller is not a tenant administrator409— Policy version conflict
The ingest API should acknowledge durable buffering, not full indexing. Search APIs must require a tenant, a bounded time range, and a query budget so one broad query cannot fan out across every shard and object-storage partition.
Database Design
Raw logs do not belong in a single relational database. Treat the relational schema as the control plane for tenants, streams, policies, and audit history. The data plane stores events in Kafka, hot search indexes, compressed chunks, and object storage.
The central modeling decision is the index namespace. Elasticsearch-style systems usually create time-partitioned indexes by tenant, service, and day. Loki-style systems store compressed chunks keyed by tenant and label set, while a smaller index maps labels and time ranges to chunks.
| tenant_id | varchar(64) | Primary key for isolation, quotas, and billing |
| name | varchar(255) | Display name |
| plan | varchar(32) | Free, enterprise, internal, or regulated |
| ingest_quota_bytes_per_day | bigint | Daily accepted ingest budget |
| query_quota_bytes_per_day | bigint | Daily scanned-byte budget |
| created_at | timestamp | Tenant creation time |
| status | varchar(20) | Active, suspended, or deleted |
| stream_id | varchar(128) | Stable id for tenant, service, environment, and label set |
| tenant_id | varchar(64) | Owning tenant |
| service | varchar(255) | Application or infrastructure component |
| environment | varchar(64) | Production, staging, development, or custom |
| labels | json | Indexed labels with bounded cardinality |
| created_at | timestamp | First seen time |
| last_seen_at | timestamp | Most recent event time |
| segment_id | varchar(128) | Index shard, Loki chunk group, or object prefix id |
| tenant_id | varchar(64) | Tenant owner |
| tier | varchar(20) | Hot, warm, or cold |
| start_time | timestamp | Inclusive segment start |
| end_time | timestamp | Exclusive segment end |
| storage_uri | text | Search index name or object storage prefix |
| bytes | bigint | Compressed or indexed size |
| status | varchar(20) | Open, sealed, compacting, searchable, or expired |
| tenant_id | varchar(64) | Primary key or part of a versioned key |
| policy_version | int | Monotonic version for safe updates |
| hot_days | int | Days in low-latency searchable tier |
| warm_days | int | Additional days in cheaper searchable tier |
| cold_days | int | Days retained in object storage |
| index_mode | varchar(64) | Full-text, label-only, hybrid, or compliance mode |
| sampling_rules | json | Priority-aware sampling and drop rules |
| updated_at | timestamp | Last policy update time |
| query_id | varchar(128) | Primary key |
| tenant_id | varchar(64) | Tenant being queried |
| user_id | varchar(128) | Caller identity |
| query_text_hash | varchar(128) | Hash of query text for audit without storing secrets unnecessarily |
| time_range_start | timestamp | Query lower bound |
| time_range_end | timestamp | Query upper bound |
| scanned_bytes | bigint | Budget and billing input |
| created_at | timestamp | Query submission time |
Indexes
- tenants.tenant_id is the primary key for control-plane lookups.
- log_streams.tenant_id, service, environment supports stream discovery and label validation.
- index_segments.tenant_id, start_time, end_time, tier supports query planning by tenant and time range.
- retention_policies.tenant_id, policy_version supports safe policy updates and rollbacks.
- query_audit_logs.tenant_id, created_at supports compliance review and quota analysis.
Relationships
Each tenant owns many log streams, retention policies, index segments, and query audit rows. Index segments are the catalog entries that tell the query planner where data lives; they point to external search clusters and object storage rather than storing log events in the metadata database.
NoSQL alternatives
For the data plane, use Kafka or Pulsar for the durable ingestion buffer, Elasticsearch or OpenSearch for full-text hot search, Loki or a custom chunk store for label-indexed logs, ClickHouse or Druid for analytical aggregation, and S3, GCS, or Azure Blob Storage for compressed cold retention.
The metadata store can be PostgreSQL, MySQL, Spanner, FoundationDB, or DynamoDB depending on consistency and scale needs. It is low-volume compared with the log events, but it is critical for authorization, query planning, and retention enforcement.
High-Level Architecture
The ingestion path is agents to load balancer to gateway to durable buffer to parsing workers to search and storage. The query path is engineers to query API to metadata, cache, search indexes, and cold object storage. Kafka decouples producers from indexing so the system can absorb bursts and survive downstream outages.
A production logging system should be designed as two related systems: an ingestion data plane and a query control plane. The ingestion plane accepts huge write volume, validates and rate-limits batches, appends to a durable buffer, and lets asynchronous workers parse, enrich, index, and archive logs. The query plane authorizes users, plans bounded searches, fans out to the right shards or chunks, and merges sorted results.
Kafka or Pulsar is the shock absorber. It allows log agents to receive fast acknowledgements after durable append while indexers can lag, scale out, or fail over. This decoupling is essential during incidents, when the very systems producing errors also produce the most logs.
The search and storage layer should be tiered. Hot data gets expensive low-latency indexing. Warm data may have fewer replicas, slower hardware, or reduced index coverage. Cold data lives as compressed chunks in object storage and is queried through slower asynchronous jobs.
Request Flow
- 1
Agent collects and batches logs
A host-level or sidecar agent tails files, reads stdout from containers, accepts application log streams, and batches events by tenant, service, and time. It adds local metadata such as hostname, pod, region, and source path before sending compressed batches.
- 2
Gateway authenticates and validates
The ingest gateway verifies tenant credentials, validates batch size and timestamps, checks schema rules, rejects obviously malformed events, and applies per-tenant and per-source rate limits. It should return partial acceptance details when only some events are invalid.
- 3
Accepted events are appended to the durable buffer
The gateway partitions events by tenant, stream, or time bucket and appends them to Kafka. It acknowledges the agent only after the broker quorum has accepted the write. This separates producer success from search-index availability.
- 4
Pipeline workers parse and enrich
Consumers read Kafka partitions, parse JSON or configured text formats, normalize fields, redact sensitive values where policy requires it, enrich with deployment and trace metadata, and assign each event to a target index or chunk stream.
- 5
Logs are indexed and archived
Error, warning, audit, and high-value application logs may be full-text indexed in Elasticsearch or OpenSearch. Lower-value high-volume logs may be written as compressed chunks with only labels indexed, then archived to object storage for retention.
- 6
Lifecycle jobs move data across tiers
As segments age, lifecycle jobs shrink, merge, snapshot, or delete indexes according to tenant policy. Hot data remains query-optimized, warm data uses cheaper resources, and cold data remains available through slower object-storage scans.
- 7
User submits a bounded query
The query API authenticates the caller, checks tenant and field permissions, requires a time range, estimates scanned bytes, and chooses whether to execute synchronously or create an asynchronous search job.
- 8
Query planner fans out and merges results
The planner uses the metadata store to find relevant indexes or chunks, sends subqueries to search shards and cold readers, merges results by timestamp, applies limits and pagination, caches safe results, and writes an audit record.
Core Components
Log Agents and Collectors
Collect logs near the source and protect applications from logging backend failures.
Agents such as Fluentd, Vector, Filebeat, or OpenTelemetry Collector tail files, read container stdout, batch events, compress payloads, retry with exponential backoff, and keep a bounded local disk buffer. They should apply basic filtering and sampling rules so a broken service cannot fill local disks indefinitely.
Ingest Gateway
The stateless front door for authentication, validation, throttling, and durable append.
The gateway terminates TLS, authenticates tenant tokens, enforces quotas, validates batch envelopes, assigns partitions, and writes accepted events to Kafka. It should avoid expensive parsing or indexing work so it can scale predictably with incoming bytes.
Durable Ingestion Buffer
Decouples producers from downstream parsing, indexing, and storage.
Kafka, Pulsar, or Kinesis absorbs bursts, provides replay for failed consumers, and exposes lag as a first-class operational signal. Topics can be separated by tenant class, environment, priority, and region to isolate high-priority logs from noisy streams.
Parsing and Enrichment Pipeline
Transforms raw events into searchable, policy-compliant records.
Pipeline workers parse structured logs, run grok-like parsers for text logs, normalize timestamp and severity fields, add metadata from service catalogs, redact sensitive fields, compute routing keys, and write to index and storage backends. They must be idempotent because Kafka replay is normal.
Search Index
Provides low-latency search over recent and warm logs.
Elasticsearch or OpenSearch stores inverted indexes for free text and selected fields. It is powerful for ad hoc debugging but expensive in CPU, memory, and disk. Shard count, mapping discipline, rollover size, and replica choices dominate reliability and cost.
Chunk and Object Storage
Stores compressed logs cheaply for long retention and replay.
Compressed chunks in S3, GCS, Azure Blob Storage, or HDFS keep cold logs durable at lower cost. Loki-style systems use labels to find relevant chunks, while cold search jobs scan and filter compressed data asynchronously when full-text indexes are no longer available.
Query Service
Authorizes, plans, executes, and merges log searches.
The query service validates the time range, checks access control, estimates query cost, finds relevant segments in metadata, fans out to search shards and chunk readers, merges results by timestamp, caches safe results, and records audit details.
Tenant Metadata and Policy Store
Stores tenants, quotas, schemas, retention policy, and segment catalogs.
The metadata store is much smaller than the log data plane but is critical for correctness. It drives authorization, ingestion limits, lifecycle jobs, index routing, query planning, and compliance retention.
Deep Dive
Durable ingestion and backpressure
The most important design choice is where the system acknowledges writes. If the gateway acknowledges before durable buffering, a gateway crash loses accepted logs. If it waits for full indexing, every index outage becomes an ingestion outage. A strong design acknowledges after Kafka quorum append.
Backpressure should be explicit and priority-aware. The gateway can return 429 with retry-after for tenants over quota, agents can buffer locally to disk for a bounded time, and the pipeline can shed low-value debug logs before audit or error logs. Kafka lag becomes the signal for autoscaling consumers, increasing sampling, or temporarily disabling expensive enrichments.
The system should also avoid infinite buffering. Local agent buffers need size limits, Kafka retention needs enough time for index recovery, and tenants need drop policies when they exceed contractual limits. Silent loss is worse than an explicit rejected count.
Parsing, schema, and enrichment strategy
Structured JSON logs are much cheaper to process than arbitrary text because fields are already named and typed. The platform should encourage services to emit structured logs with standard fields such as timestamp, level, service, environment, trace_id, request_id, customer_id hash, and region.
Unstructured logs still matter for legacy systems. Pipeline workers can use configured parsers, but parser failures must not block ingestion. Store the original message, add a parse_error flag, and route failures to a dead-letter stream for rule fixes.
Enrichment increases query value but can also increase cost and cardinality. Add stable metadata from deployment systems and service catalogs, but be careful with high-cardinality labels such as user_id, request_id, and full URL. Those belong in searchable fields or raw payloads, not always in index labels.
Elasticsearch full-text index versus Loki label index
Elasticsearch and OpenSearch build inverted indexes for text and selected fields. This is excellent for ad hoc searches such as finding an error phrase across services, but it multiplies storage and CPU cost. Mapping explosions, high-cardinality fields, and too many small shards can take down the cluster.
Loki-style systems intentionally index only labels such as tenant, service, environment, region, and level. The actual log lines are compressed into chunks in object storage. Queries first use labels and time ranges to find chunks, then scan those chunks. This is cheaper and operationally simpler at very high volume, but arbitrary free-text search over broad time ranges is slower.
A premium answer proposes a hybrid. Keep full-text indexes for recent high-value logs, errors, audits, and selected services. Store debug and trace-heavy logs as label-indexed chunks. Let tenants choose policies by value and budget rather than forcing one indexing model on all data.
Storage tiers and lifecycle policy
Hot storage is optimized for low query latency and write throughput. It uses fast disks, enough replicas, and recent time-based indexes. This is the most expensive tier, so retention should be short, often 3 to 14 days depending on customer needs.
Warm storage keeps data searchable but cheaper. It may use slower nodes, fewer replicas, force-merged segments, searchable snapshots, or reduced field indexes. Query latency is higher, but incident investigations can still search the last few weeks.
Cold storage is compressed object storage. It is durable and inexpensive per byte, but search is slower and should run as an asynchronous job. Lifecycle jobs must update the segment catalog atomically so queries know which tier owns each time range.
Query planning and tail latency
Log search is dangerous because a single query can fan out to thousands of shards and scan terabytes. Require tenant scope and time bounds. Estimate query cost before execution using segment metadata, field statistics, and tier information.
For hot queries, fan out to relevant shards in parallel and stream the first results as soon as possible. For cold queries, create an asynchronous job with progress, partial results, and cancellation. Use time-sorted pagination so users can continue investigations without rescanning from the beginning.
Protect the cluster with query budgets, concurrency limits, shard fan-out caps, result limits, and cancellation on client disconnect. Cache common field lists, recent dashboard queries, and small repeated searches, but avoid caching sensitive cross-tenant data.
Multi-tenancy, sampling, and log storms
The platform will see log storms during bad deploys, dependency outages, retry loops, and security incidents. Those are exactly the moments when logs are most useful, so the design should degrade by priority instead of failing uniformly.
Use per-tenant and per-service quotas, priority classes, and sampling rules. For example, keep all ERROR and audit logs, sample DEBUG logs after a threshold, and aggregate repeated identical messages into counters. Make dropped and sampled counts visible so engineers do not mistake absence of logs for absence of failures.
Isolation can be logical or physical. Small tenants can share Kafka topics and index clusters with strict quotas. Regulated or high-volume tenants may need dedicated topics, index namespaces, encryption keys, or even clusters. Query isolation is as important as ingest isolation because one expensive search can starve everyone.
Scaling
Starter: single region with managed search
Run agents, a stateless ingest API, a small Kafka cluster or managed stream, a few parsing workers, and a managed Elasticsearch or OpenSearch cluster. Keep retention short, index a limited field set, and store raw archives in object storage.
Growth: high-volume multi-service platform
Partition Kafka by tenant and stream, split hot indexes by tenant and day, introduce lifecycle policies, add query budgets, and scale pipeline workers independently. Add a metadata store for retention, segment catalogs, and schema management.
Large scale: hundreds of TB per day
Separate tenants by class, use multiple Kafka clusters or topics, introduce hybrid indexing, run dedicated hot and warm search clusters, compact logs into object-storage chunks, and make cold queries asynchronous. Autoscale workers based on lag and bytes per second.
Global scale: multi-region ingestion and query
Ingest logs in the nearest region, replicate critical Kafka topics or archived chunks across regions, keep tenant metadata globally consistent, and route queries to the region that owns the relevant time range. Use regional isolation so a search or ingest incident does not become global.
Extreme scale: petabytes per day with cost controls
Use label-first indexing for high-volume logs, reserve full-text indexing for high-value streams, push sampling and aggregation to agents, place premium tenants on dedicated capacity, and continuously optimize retention based on query frequency and business value.
Bottlenecks & Optimizations
Index cluster write saturation
Buffer through Kafka, batch index writes, tune refresh intervals, roll over indexes by size, reduce indexed fields, isolate heavy tenants, and route lower-value logs to label-indexed chunks instead of full-text indexes.
Kafka partition hot spots
Partition by a balanced key such as tenant plus stream hash rather than only tenant. Use more partitions for large tenants, separate priority topics, monitor broker disk and network utilization, and rebalance before partitions hit sustained throughput limits.
High-cardinality labels and mapping explosion
Reject or demote labels with unbounded cardinality, enforce schema contracts, cap dynamic fields, route request_id and user_id to raw fields instead of labels, and alert on sudden field growth.
Broad queries scanning too much data
Require time ranges, estimate scanned bytes, limit shard fan-out, use async jobs for cold searches, add query cancellation, and educate users to filter by tenant, service, level, and labels before free-text terms.
Object storage cold-query latency
Partition chunks by tenant and time, maintain compact segment catalogs, store bloom filters or lightweight indexes for common fields, parallelize scans, and return partial results with progress rather than blocking interactive requests.
Log storms from retry loops or bad deployments
Apply adaptive sampling, per-service rate limits, duplicate-message aggregation, and priority queues. Preserve all audit and error logs where possible while sampling debug noise and exposing drop counters to the owning team.
Failure Handling
Search index cluster outage
Keep accepting logs into Kafka as long as buffer capacity remains. Pause or slow index consumers, alert on lag, scale replacement capacity, and replay from Kafka after recovery. Recent search may be stale, but ingestion should not fail immediately.
Kafka broker or partition failure
Use replication across availability zones, minimum in-sync replica settings, rack-aware placement, and producer retries with idempotence. If quorum is unavailable, gateways should reject or throttle rather than pretending logs were durably accepted.
Pipeline parser bug corrupts fields
Version parser rules, canary new pipelines, preserve raw messages, write failed parses to a dead-letter stream, and support replay from Kafka or object storage after fixing the parser. Do not make destructive transformations irreversible.
Object storage outage
Hot search can continue for recent indexed data, but cold archive writes may queue locally or in Kafka for a bounded time. Lifecycle jobs should pause, query APIs should mark cold tiers degraded, and retries should be idempotent.
Tenant exceeds quota during incident
Apply the tenant policy: preserve high-priority logs, sample lower-priority levels, return 429 or partial acceptance to agents, and make drop counts visible. Enterprise tenants may have burst credits or emergency quota overrides.
Regional ingestion failure
Agents should fail over to a secondary region when configured, preserving tenant identity and ordering best-effort. The secondary region writes to its local buffer and later reconciles segment metadata so queries can find the data.
Security
Tenant authentication and authorization
Agents authenticate with scoped tokens or mTLS certificates. Users authenticate through identity providers, and the query service enforces tenant, environment, service, and field-level permissions before returning logs.
Sensitive data handling
Logs often contain tokens, emails, IP addresses, payment references, or customer identifiers. Redact at the agent or pipeline where possible, classify fields, restrict access to sensitive streams, and avoid storing query text with raw secrets in audit logs.
Encryption and key isolation
Use TLS in transit and encryption at rest for Kafka, indexes, metadata, and object storage. High-security tenants may require separate encryption keys, dedicated storage prefixes, and stricter key rotation policies.
Auditability
Every search, export, policy change, and privileged access should create an audit record with caller, tenant, time range, fields, scanned bytes, and purpose. Audit logs should have stronger retention and tamper resistance than ordinary debug logs.
Abuse and exfiltration protection
Rate-limit large exports, require approval for broad historical searches, watermark exports where appropriate, and alert on unusual query patterns such as a user scanning many tenants or downloading entire audit streams.
Data residency and deletion
Retention and residency policies must control where logs are stored and when they are deleted. Lifecycle jobs should prove deletion from hot, warm, cold, snapshots, and derived indexes where regulations require it.
Tradeoffs
Pros
- +Kafka decouples log producers from indexing and gives replay for failed consumers.
- +Tiered storage keeps recent debugging fast while making long retention affordable.
- +Hybrid indexing lets teams pay for full-text search only where it is valuable.
- +Per-tenant quotas and query budgets protect shared infrastructure from noisy neighbors.
- +Structured logging and enrichment make logs useful for correlation with metrics and traces.
Cons
- −Full-text search at hundreds of TB per day is expensive in CPU, memory, and disk.
- −Loki-style label indexing is cheaper but slower for broad arbitrary text searches.
- −Kafka buffering adds operational complexity and requires careful lag management.
- −Sampling and rate limiting can hide individual debug events if policies are not transparent.
- −Multi-region ingestion complicates query planning, retention, and compliance controls.
Alternatives
Alternative one is a simple ELK stack where Logstash writes all events into Elasticsearch. It is easy to understand and powerful for search, but it becomes expensive and fragile at very high volume unless mappings, shards, and retention are tightly controlled.
Alternative two is a Loki-style architecture with label indexes and compressed chunks in object storage. It is cost-effective for high-volume logs when users usually query by labels and time, but broad free-text search over cold data is slower.
Alternative three is an analytics database such as ClickHouse for structured logs. It can be excellent for aggregations, dashboards, and columnar scans, but it is not a drop-in replacement for arbitrary full-text debugging unless paired with text indexes or careful schema design.
When not to use this design
Do not use the logging system as the primary source for precise metrics, distributed tracing, financial ledgers, or user-facing audit state. Logs are high-volume diagnostic evidence and may be sampled, delayed, redacted, or retained by policy. Use metrics for alerting math, traces for request causality, and transactional databases for authoritative business state.
Follow-up Questions
Where should the system acknowledge a log batch?
After durable append to Kafka or an equivalent replicated buffer. Acknowledging before that can lose accepted logs, while waiting for indexing couples producers to search-cluster failures.
Why not index every field of every log forever?
The storage, CPU, and memory cost is too high at hundreds of TB or PB per day. Full-text indexes should be reserved for hot and high-value logs, while older or lower-value logs move to cheaper chunked object storage.
How do you handle a log storm during a bad deployment?
Use per-tenant and per-service quotas, Kafka buffering, autoscaling based on lag, priority classes, duplicate aggregation, and adaptive sampling. Preserve ERROR and audit logs where possible and sample DEBUG noise with visible dropped counts.
What is the difference between Elasticsearch and Loki for this problem?
Elasticsearch builds full-text and field indexes, so ad hoc text search is fast but expensive. Loki indexes labels and stores compressed chunks, so it is cheaper and scales well for label-scoped searches but broad text scans are slower.
How do you prevent one tenant from hurting others?
Enforce ingest quotas, query budgets, concurrency limits, separate topics or clusters for large tenants, per-tenant index namespaces, and fair scheduling. Monitor tenant-level lag, drops, scanned bytes, and shard pressure.
How are late-arriving logs handled?
Use event timestamps and ingestion timestamps separately. Route events to the correct time bucket when the lateness is within policy, keep a small window open for updates, and send very late data to cold storage or a late-events index.
How do you search cold logs efficiently?
Make cold searches asynchronous, use segment metadata to prune by tenant and time, store chunks with useful prefixes, add lightweight bloom filters or field stats where justified, scan in parallel, and return partial results with cancellation.
Company Variations
Amazon
Amazon interviewers often push on operational excellence: CloudWatch-style ingestion, Kinesis or Kafka buffering, multi-AZ durability, tenant quotas, cost controls, and what alarms fire when index lag or dropped logs increase.
Netflix
Netflix may frame the problem around massive microservice fleets, regional outages, noisy deploys, and fast incident debugging. Emphasize resilience during log storms, high-cardinality service metadata, adaptive sampling, and developer search experience.
Databricks
Databricks is likely to probe data scale, lakehouse storage, structured logs, streaming ingestion, schema evolution, retention, and query engines over object storage. Be ready to compare indexing with columnar or chunked storage for PB-scale data.
Snowflake
Snowflake may focus on multi-tenant data isolation, cost-based query execution, storage-compute separation, governance, and historical search. Discuss query budgets, cold-tier scans, auditability, and metadata-driven pruning.
Interview Tips
Lead with the two-path framing: ingestion must be durable and elastic, while query must be bounded and cost-aware. Draw Kafka early because it is the main decoupling mechanism. Then explain what gets full-text indexed, what becomes compressed chunks, and how retention moves data through hot, warm, and cold tiers. Use the log-storm scenario to demonstrate backpressure, sampling, and tenant isolation.
What interviewers expect
- ✓Start with agents, ingestion gateway, durable buffer, pipeline workers, search indexes, and object storage.
- ✓State when data is acknowledged and how replay works after downstream failures.
- ✓Show real scale numbers for millions of events per second and hundreds of TB per day.
- ✓Discuss query planning, shard fan-out, time bounds, and cold searches.
- ✓Explain tenant isolation, quotas, sampling, and security.
- ✓Choose an indexing strategy based on cost, freshness, and query patterns.
Common mistakes
- !Acknowledging logs only after Elasticsearch indexing completes.
- !Ignoring Kafka or another durable buffer and writing agents directly to the search cluster.
- !Full-text indexing every field forever without discussing cost or lifecycle tiers.
- !Forgetting multi-tenancy, quotas, and noisy-neighbor protection.
- !Treating DEBUG logs and audit logs as having the same durability requirements.
- !Allowing unbounded queries without time ranges, budgets, or cancellation.
Red flags
- ×No concrete capacity math for events per second, bytes per day, or storage retention.
- ×No backpressure or drop policy for log storms.
- ×No explanation of Elasticsearch versus Loki-style indexing tradeoffs.
- ×No plan for sensitive data redaction and access control.
- ×No lifecycle policy for hot, warm, and cold tiers.
Revision Notes
- A logging system ingests events from agents, durably buffers them, parses and enriches them, indexes recent high-value data, and archives compressed logs for retention.
- Acknowledge ingestion after Kafka quorum append, not after full indexing and not before durable storage.
- At 200,000 hosts and 10 events per second each, expect 2M events per second average, 172.8B events per day, and about 173 TB per day at 1 KB per event.
- A 5x incident burst reaches 10M events per second and about 10 GB per second raw input.
- Hot full-text indexing is expensive: 173 TB per day times 1.5 index expansion times 2 copies is about 519 TB per hot day.
- Elasticsearch or OpenSearch gives powerful full-text search but high cost. Loki-style label indexing plus chunks is cheaper but slower for broad text search.
- Use hot, warm, and cold tiers. Hot is fast and expensive, warm is cheaper but searchable, and cold is compressed object storage with asynchronous queries.
- Protect the platform with tenant quotas, query budgets, adaptive sampling, redaction, audit logs, and high-cardinality label controls.
- Query APIs should require tenant and time bounds, estimate scanned bytes, fan out only to relevant segments, and cancel runaway searches.
Flashcards
Quiz
0/7 answered
1.Which component best decouples log producers from indexing failures?
2.At 200,000 hosts and 10 events per second per host, what is the average event rate?
3.Why is a Loki-style label index cheaper than full-text indexing?
4.What is the best default behavior for a broad historical query over cold logs?
5.Which field is usually dangerous as an indexed label because of high cardinality?
6.If the search index is down but Kafka is healthy, what should the system do?
7.What is the strongest reason to expose dropped or sampled log counts?
Cheat Sheet
Goal: collect, buffer, process, search, and retain logs from many services at millions of events per second.
Ingestion path: agents to load balancer to ingest gateway to Kafka to pipeline workers. Acknowledge after durable buffer append.
Scale example: 200,000 hosts times 10 events per second is 2M events per second. At 1 KB per event, that is about 173 TB per day. A 5x burst is 10M events per second and 10 GB per second raw.
Buffer: Kafka or Pulsar absorbs bursts, provides replay, and exposes lag. Separate topics by tenant class, priority, region, or environment when isolation matters.
Processing: parse JSON, handle legacy text, normalize fields, enrich with tenant and deployment metadata, redact sensitive values, and route by policy.
Indexing: Elasticsearch or OpenSearch gives fast full-text search but high cost. Loki-style labels plus compressed chunks are cheaper but slower for broad text search. Hybrid is often best.
Storage tiers: hot for recent low-latency search, warm for cheaper searchable history, cold object storage for long retention and asynchronous scans.
Query: require tenant and time bounds, estimate scanned bytes, limit shard fan-out, use async jobs for cold data, cache safe repeated results, and audit every access.
Reliability: keep ingestion available when search is degraded, replay from Kafka, preserve raw logs before risky parsing, and monitor lag, rejected batches, indexing errors, and dropped events.
Security: authenticate agents and users, enforce tenant access, redact secrets, encrypt at rest and in transit, audit searches and exports, and comply with retention and residency policy.
References
- BookDesigning Data-Intensive Applications — Martin Kleppmann
- BlogThe Log: What every software engineer should know about real-time data's unifying abstraction — Jay Kreps
- DocsElasticsearch Guide — Elastic
- DocsGrafana Loki Documentation — Grafana Labs
- PaperThe Tail at Scale — Jeffrey Dean and Luiz Andre Barroso