Release Notes
Version 1.2.1
📅 Release Date
August 24, 2026
📖 Overview
An anonymized sample of 330 production log records from several Workers, read line by line. Most of what follows is a consequence of something in that file rather than a design idea.
Half the line was already in $metadata. Cloudflare wraps every stored record in an envelope carrying the script
name as service, the severity as level, the message, and trigger — the method and path joined. Two of those it
lifts from the top of the object we log; the rest it knows on its own. A Worker restating them pays to ingest a column
the dashboard already renders.
The Message column was blank. $metadata.message matched source.message on 171 of 171 rows and source.event on
0 of 136 — the promotion reads the key message and nothing else. 1.2.0 renamed it, so nothing fed the column.
46% of rows carried none of our fields. 152 of 330 were invocation logs, which duplicate what request.completed
already says while leaving every application column empty.
And 15 of 28 distinct client addresses were fake. More on that below.
⚠️ Breaking Changes
- The payload is flattened onto the line by the kernel, so
data.statusisstatus. Saved queries, dashboard columns and Logpush consumers readingdata.drop the prefix service,colo,countryanddurationMsare off the line by default. They were unconditional; they are now one word inLOG_FIELDSaway.$metadata.servicecovers the first inside Workers Logsinvocation_logs: falseis recommended in every Worker'swrangler.json. It removes 46% of rows. The cost is real:outcome,cpuTimeMsandwallTimeMsexist only on those rows and leave Workers Logs with them —request.completed/request.failedcovers the outcome, and traces cover the timing
🚀 Features
LOG_FIELDS— which ambient fields a line carries, declared per deployment inwrangler.json:{ "vars": { "LOG_FIELDS": "requestId,method,path,ip,status" } }cloudflareDefaults.logging.fieldssupplies that same list as the default, so a Worker that sets nothing carries the correlation id, the method, the path, the client address and the status — and leavesservice,colo,country,component,durationMsandtimeto be asked for.That default is right only while the lines stay inside Workers Logs. A deployment shipping through Logpush, exporting OTel, or writing to a file should add
service: a record that only means something inside one vendor's console is not a record.*keeps everything;""keeps nothing.The mechanism is the kernel's and vendor-neutral. What is here is the judgement about this platform — which is the only thing that would be wrong to carry to another one.
A readable Message column.
logToAnalyticscomposes a sentence:Level Message info Request completed: POST / (200) warn Request completed: POST /api/v1/search (400) error Request failed: POST /api/v1/resource (500)eventis untouched and still what a query filters on. The sentence restates the method, path and status that are already fields on purpose: you read the message, you filter on the fields.
🔧 Enhancements
- None. Everything this release does is a fix or a field-level default change — see Breaking Changes and Bug Fixes.
🐛 Bug Fixes
The client address was synthetic for a third of traffic. 15 of 28 distinct IPs we recorded fell in
240.0.0.0/4, and on 50 of 150 invocation rowscf-connecting-ipwas byte-identical tocf-pseudo-ipv4:cf-connecting-ip 251.130.12.42 ← what we logged cf-pseudo-ipv4 251.130.12.42 ← identical: it is synthetic cf-connecting-ipv6 2a09:bac1:7680:1258::272:48 ← the real clientA zone with Pseudo-IPv4 set to overwrite headers replaces
CF-Connecting-IPfor every IPv6 client. The replacement routes nowhere, geolocates to nothing, and need not be stable between requests — worse than logging no address at all, because it reads as a real answer and an investigation follows it to nobody.CloudflareRequestMetadatanow readscf-connecting-ipv6first, so the true address wins whatever the zone is set to.authentication.failedlogged atinfoin the shared middleware's sibling — four production failures that never reached a severity filter. Nowwarn.
🔐 Security
- None. See Bug Fixes above for the client-address correction.
🧪 Tests
- None documented. See the 1.2.1 change log for the underlying diff.
📚 Documentation
- None.
⬆️ Upgrading
{
"vars": {
"SERVICE_NAME": "my-worker",
"LOG_LEVEL": "info",
"LOG_FIELDS": "requestId,method,path,ip,status"
},
"observability": {
"enabled": true,
"logs": { "head_sampling_rate": 1, "invocation_logs": false },
"traces": { "enabled": true, "head_sampling_rate": 0.1 }
},
"upload_source_maps": true
}
Then point the dashboard at $metadata.service, $metadata.level, $metadata.message, and the plain method, path,
ip and status columns. Nothing needs a $workers. prefix any more.
🚨 Known Issues
- None
📦 Dependencies
@bayudwiyansatria/coremoves to^1.2.1.
👥 Contributors
- Bayu Dwiyan Satria
🙏 Acknowledgments
Special thanks to all contributors and the open-source community for their support.
For more information, visit the project's GitHub repository.