REPORT line to CloudWatch Logs. These ~200-byte lines look free, but at scale they routinely account for 20–60% of a serverless platform’s CloudWatch Logs bill — sometimes more than the Lambda compute itself. The fix is not “log less” in your code. It is tuning system-level logging, retention, and destination. Below: the math, detection queries, and five fixes that took one client from €4,310/month to €890/month with zero observability loss.What is a Lambda REPORT log line?
Every time an AWS Lambda function finishes executing, the Lambda service writes a REPORT line into the function’s CloudWatch log group. It is emitted by the platform, not by your code, and you cannot disable it from inside the function. A typical REPORT line looks like this:
REPORT RequestId: 5b3f9b8e-... Duration: 123.45 ms Billed Duration: 124 ms Memory Size: 512 MB Max Memory Used: 89 MB Init Duration: 187.32 ms
That is roughly 170–230 bytes per invocation, depending on whether Init Duration is present (cold starts) and whether you have enabled X-Ray tracing (which adds an XRAY TraceId line).
Two cousins ride along with REPORT by default:
START RequestId: …— ~90 bytesEND RequestId: …— ~50 bytes
Together, the platform writes roughly 300 bytes of “system” log per invocation, before your application has emitted a single character.
The math: why 300 bytes per invocation matters
CloudWatch Logs pricing (us-east-1, as of 2026):
| Item | Price |
|---|---|
| Ingestion | $0.50 / GB |
| Archive storage (after gzip) | $0.03 / GB-month |
| Logs Insights query | $0.005 / GB scanned |
In eu-central-1, ingestion is roughly $0.57/GB.
Now do the multiplication. A modest serverless workload — a single Lambda invoked 100 million times per month (an API at ~38 req/s) — emits:
100,000,000 × 300 bytes ≈ 30 GB/month of platform-emitted log lines alone.
At $0.50/GB ingestion, that is $15/month per function, just for START / END / REPORT — before any application logs, before any retention costs.
Spread across 50 active Lambda functions of similar volume, you are paying ~$750/month for log lines your code did not write. We have audited customer accounts where this number was a five-figure annual line item and nobody on the team knew.
Why people miss it
Three reasons it stays hidden:
- CloudWatch billing is bundled. The AWS bill shows “CloudWatch Logs — DataProcessing-Bytes” — not “Lambda emitted REPORT lines.” You have to disaggregate.
- The lines are tiny. 200 bytes per invocation feels invisible. It only matters at scale, and at scale it becomes a leak.
- Engineers focus on application logs. Code reviews catch
console.log(massiveObject). Nobody reviews the implicit logging AWS does on your behalf.
How to detect the leak in your account
Run this three-step audit.
1. Find the largest log groups
aws logs describe-log-groups
--query 'logGroups[?starts_with(logGroupName,`/aws/lambda/`)].[logGroupName,storedBytes,retentionInDays]'
--output table
Sort by storedBytes descending. The top 10 typically account for >80% of your Lambda log cost.
2. Measure ingestion (not just storage)
Storage is post-compression and post-retention; ingestion is what you actually pay for. Use CloudWatch metrics:
aws cloudwatch get-metric-statistics
--namespace AWS/Logs
--metric-name IncomingBytes
--dimensions Name=LogGroupName,Value=/aws/lambda/your-function
--start-time $(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ)
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ)
--period 86400 --statistic Sum
Sum daily values × $0.50 / 1e9 → monthly ingestion spend per group.
3. Estimate the REPORT share
Use CloudWatch Logs Insights:
filter @type = "REPORT"
| stats count(*) as report_count, sum(strlen(@message)) as report_bytes by bin(1d)
Compare report_bytes to total IncomingBytes. If REPORT alone is more than 25% of ingest, you have a leak.
Five concrete fixes
Fix 1 — Set explicit log retention everywhere
By default, Lambda log groups retain logs forever. Storage at $0.03/GB-month compounds indefinitely. Set retention with a one-liner:
aws logs put-retention-policy
--log-group-name /aws/lambda/your-function
--retention-in-days 14
For production observability, 14–30 days is usually enough; archive longer history to S3 (see Fix 4). Enforce this org-wide with an AWS Config rule or a Terraform module that requires retention_in_days.
Fix 2 — Use Lambda Advanced Logging Controls
Since November 2023, Lambda lets you set log level and format per function. Switch to JSON-structured logs and raise the system log level to WARN:
resource "aws_lambda_function" "api" {
# ...
logging_config {
log_format = "JSON"
application_log_level = "INFO"
system_log_level = "WARN"
}
}
Effect: START and END lines are suppressed for healthy invocations. (REPORT is always emitted — it is the billing record — but you have already cut ~140 bytes per invocation.) On a 100M-invocation function that alone reclaims ~14 GB/month, or $7.
Fix 3 — Sample, do not suppress, application logs
Do not if (Math.random() < 0.01) log(...) by hand. Use AWS Lambda Powertools (Python, TypeScript, Java, .NET) which has a first-class sampler:
import { Logger } from "@aws-lambda-powertools/logger";
const logger = new Logger({ sampleRateValue: 0.05 }); // 5% sample
Powertools also lets you log everything for a single request when an error fires — so you keep debuggability without paying for noise.
Fix 4 — Route hot log groups to S3 via Firehose
For high-volume functions, subscribe the log group to a Kinesis Data Firehose that writes gzip-compressed batches to S3:
Lambda → CloudWatch Logs → Subscription Filter → Firehose → S3
Then set CloudWatch retention to 1–3 days (just enough for live tailing) and query history with Athena. Firehose-to-S3 ingestion is $0.029/GB versus CloudWatch’s $0.50/GB — a 17× reduction for archival.
Fix 5 — Audit and tag
You cannot optimise what you cannot attribute. Tag every Lambda function with cost-center, team, and service, propagate the tags to the log group, and build a Cost Explorer view filtered by service: AWS-CloudWatch grouped by tag. Make the per-team CloudWatch bill visible in your engineering wiki. Visibility alone tends to cut the bill 30–50% within a quarter.
Worked example: a real (anonymised) audit
A Berlin fintech client ran 84 Lambda functions in eu-central-1. CloudWatch Logs spend was €4,310/month. Breakdown after audit:
| Bucket | Monthly cost | Share |
|---|---|---|
Application logs (real logger.info, etc.) |
€1,180 | 27% |
| START / END / REPORT system lines | €2,470 | 57% |
| X-Ray annotations | €310 | 7% |
| Indefinite-retention storage | €350 | 8% |
After applying Fixes 1, 2, and 4: monthly spend dropped to €890 — an 80% reduction with zero observability loss for production debugging. Payback on the engineering time: under three weeks.
When NOT to do this
A few honest caveats:
- Low-volume functions. If a function is invoked <100k times/month, the bill is rounding-error money. Optimise it last.
- Compliance-bound workloads. Some regulators require all logs in a tamper-evident store with multi-year retention. Use S3 Object Lock and KMS — but you can still cut CloudWatch’s role to short-term hot storage.
- You do not have telemetry hygiene yet. Sampling logs before you have structured logging and traces will hurt incident response. Add structure first, then sample.
FAQ
Can I disable Lambda REPORT logs entirely?
No. The REPORT line is emitted by the Lambda service itself and is the source-of-truth for invocation duration and memory metrics. You can route the log group’s output elsewhere (Firehose → S3) or aggressively shorten retention, but you cannot tell Lambda to stop emitting it.
How much do REPORT logs cost per Lambda invocation?
Roughly $0.0000001 per invocation at $0.50/GB ingestion and ~200 bytes per line — about $0.10 per million invocations for REPORT alone. Add START + END and it doubles to ~$0.20 per million.
Does Lambda Advanced Logging help with REPORT lines?
It suppresses START and END when system_log_level is WARN, but REPORT is always emitted. Savings come from killing the START/END noise and from JSON-formatted application logs being more compressible and queryable.
Is CloudWatch Logs Insights expensive?
At $0.005/GB scanned, Insights is cheap per query but expensive if dashboards re-scan the same data every minute. Cache results, narrow time ranges, and use metric filters for recurring counts.
Should I just send all logs to S3 and skip CloudWatch?
For archival, yes — S3 + Athena is 10–20× cheaper. But CloudWatch’s live tailing, metric filters, and alarms remain useful for the last 24–72 hours. The right architecture is usually both: short retention in CloudWatch for ops, long retention in S3 for forensics.
How do I monitor that the fix stays in place?
Set a CloudWatch alarm on IncomingBytes per log group, with a per-function threshold. Add an AWS Config rule that flags any new log group with retention > 30 days. Review monthly in your FinOps cadence.
The takeaway
The most expensive AWS “code” you are running is often code you never wrote. Lambda’s automatic START, END, and REPORT lines are individually trivial and collectively significant. Auditing them is a one-day project; the savings compound forever.
If you would like a fixed-fee CloudWatch + Lambda cost audit on your account, we offer cloud architecture reviews and tech-strategy audits from our Berlin practice — typical engagement is two weeks and identifies 25–60% of annual CloudWatch spend as recoverable. Book a free 30-minute call →
