Deploy and operate Hono
Observe portable applications
Emit request ids, structured errors, durations, and domain metrics through runtime-appropriate sinks.
Portable route code still needs logging. Console output and platform tracing differ by runtime, so define a narrow interface and adapt it at the edge.
Both deployments report route, status, duration, and request id without logging bookmark titles or session cookies:
function createLogger(sink) {
return {
info(event, fields = {}) {
sink.write(JSON.stringify({ level: 'info', event, ...fields }))
},
error(event, fields = {}) {
sink.write(JSON.stringify({ level: 'error', event, ...fields }))
}
}
}
const accessLog = async (c, next) => {
const start = Date.now()
await next()
c.get('logger').info('request.complete', {
requestId: c.get('requestId'),
route: c.req.path,
status: c.res.status,
durationMs: Date.now() - start
})
}
Send one request and inspect what comes back:
curl -s -D - http://localhost:3000/bookmarks -o /dev/null
You should see x-request-id: ... in response headers. On Node stdout, a line like:
{"level":"info","event":"request.complete","requestId":"a1b2-...","route":"/bookmarks","status":200,"durationMs":4}
Workers Tail shows the same JSON shape if you log through the injected sink instead of scattering console.log in handlers.
Redact by default. Log ids and status codes, not session cookies or POST bodies with passwords.
A realistic failure: access logging middleware is registered after the routes:
app.get('/bookmarks', handler)
app.use('*', accessLog) // too late for routes above
Curl still returns 200, but no access line appears for /bookmarks. Fix by moving app.use('*', accessLog) above route registration.
Trigger a 500 on both deployments with the same curl that provokes a storage error. Trace from x-request-id in the response to the matching log line. If you cannot connect them in under a minute, tighten the middleware order and id wiring.
Inject the logger through createApp the same way you inject the bookmark store. Node passes { write: (line) => process.stdout.write(line + '\n') }. Workers pass a sink that forwards to Tail.
Never log full Cookie headers or POST bodies in production access logs. If debug logging is required, gate it behind an env flag and redact known secret field names.
Compare Node stdout and Workers Tail side by side for the same failing bookmark POST. Field names should match so log queries work on both platforms.
Try this on your own project: add access logging middleware and confirm a 500 includes request id in logs but not stack details in the JSON body.
Lesson completed