Your Logs Are Burning Through Your Budget — And You Probably Didn’t Notice
Logging is not free. This article breaks down the hidden CPU, memory, storage, and ingestion costs of verbose logging and offers concrete strategies — like lazy evaluation and sampling — to slash your observability bill without losing debugging power.
Advertisement
Your Logs Are Burning Through Your Budget — And You Probably Didn’t Notice
Logging is the duct tape of production. It’s cheap when you write it, invisible when you run it, and a monster when the bill arrives.
Most teams treat logging as a free resource. A few info() calls here, a json.dumps() there. But in a modern microservice architecture, the overhead compounds silently. Storage costs mount. CPU cycles get eaten by string formatting. Observability providers charge per gigabyte ingested. Suddenly your "simple" debug log is costing more than your database.
The True Cost of One Log Line
Every log has four hidden price tags:
- CPU time — String interpolation, JSON serialization, and I/O locking. A
logging.debug(f"Data: {huge_dict}")can spike latency by milliseconds per call. - Memory — Buffers, async handlers, and queue backlogs. When logs pile up faster than they drain, containers OOM-kill themselves.
- Storage — Raw log files on disk or in cloud storage. With retention policies, even a 1 GB/day stream costs noticeable money over a year.
- Ingestion — SaaS observability tools charge per GB ingested. A verbose service can jump from $50/month to $5,000/month overnight.
Why "We'll Fix It Later" Is a Costing Strategy
I’ve seen a team in a mid-sized e-commerce company deploy a new microservice that logged the entire request and response body for every API call — including binary blobs and credit-card-like data. Their monthly Datadog bill tripled in two weeks. The logs were never read. Nobody noticed until the finance team asked why their observability spend hit $12,000.
The worst part? They had to pay to delete the old logs because the provider’s retention windows were fixed at 30 days.
The Debug Level Trap
Many teams follow the "debug in dev, info in prod" standard. That’s fine. The trap is that in production, logging.DEBUG still gets evaluated even when the handler discards it.
import logging
logger = logging.getLogger(__name__)
# Bad: string is always formatted
logger.debug(f"Payload: {json.dumps(payload)}")
# Better: lazy evaluation
logger.debug("Payload: %s", json.dumps(payload))
# Best: conditional + lazy
if logger.isEnabledFor(logging.DEBUG):
logger.debug("Payload: %s", json.dumps(payload))
The third form avoids CPU work unless the log level is active. Small change. Massive savings at scale.
Structured Logging: The Double-Edged Sword
Structured logs (JSON, ndjson, or structured text) are great for searchability. But every key-value pair adds overhead.
Consider:
{"timestamp": "2025-03-25T10:00:00Z", "level": "INFO", "message": "User signed in", "user_id": 12345, "ip": "192.168.1.1", "user_agent": "Mozilla/5.0", "session_id": "abc-def", "request_id": "xyz-789"}
That’s 200 bytes per log line. If your service does 10,000 requests/second, that’s 2 MB/second of raw log data. Over a month: ~5.2 TB. At $0.50/GB (typical SaaS pricing), that’s $2,600/month just for that one log type.
Now multiply by every service in your stack.
Teams That Fixed It Saved Big
- A fintech startup reduced log volume by 70% by moving from
infotowarningfor all non-critical paths and removing duplicate logs in hot code paths. Their monthly observability bill dropped from $4,200 to $1,100. - A SaaS platform implemented log sampling — 10% of requests get full detail, others get a single-line summary. They reduced storage costs by 90% and kept debugging capability intact.
- An IoT company replaced verbose Python logging with structured binary protobuf feeds that they parsed only for incidents. CPU usage on edge devices dropped 12%, improving battery life.
Practical Steps to Stop Bleeding Log Costs
- Audit your high-volume loggers — Find which lines fire the most. Profile with
loggingmodule’s internal counters or simple timers. - Use log sampling, not log slamming — For high-frequency events (health checks, polling loops, retries), log only a representative fraction.
- Remove PII and large objects — Sanitize before logging. Never dump request bodies or database rows by default.
- Set retention limits in code — Auto-expire logs in logrotate, S3 lifecycle policies, or your observability provider’s settings.
- Monitor your log cost per request — A cheap Python microservice with 5µs log overhead can become a $0.01-per-request disaster at 10,000 req/s.
The Bottom Line
Logging verbosity isn’t a coding style choice — it’s a financial decision. The next time you type logger.info(f"Processing {user} with {stuff}"), ask yourself: Would I pay $0.001 for this line to be stored for 30 days? If the answer is no, change it. Your budget — and your infra team — will thank you.
Advertisement
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.