Skip to content

Commit

Permalink
refactor: Enhance logger with structured req and res info (#42)
Browse files Browse the repository at this point in the history
- Improved logger to capture detailed metadata for each request and
response, including method, URL, transport, address, location, timezone,
platform, and user agent.
- Combined `city`, `region`, and `country` into a single `location`
field for readability.
- Ensured consistent handling of null values for improved structure and
parsing.
- Included error messages in logs for status codes >= 400 and response
cookies.
- Example Log:
  {
    "level": "info"/"error",
    "message": "[200] - [GET] http://localhost:55604/",
    "details": {
      "method": "GET",
      "url": "http://localhost:55604/",
      "transport": null,
      "address": null,
      "port": null,
      "addressType": null,
      "location": "Chicago, Illinois, US",
      "timezone": "America/Chicago",
      "reqCookies": ["access_token"],
      "platform": "macOS",
      "UA": "Mozilla/5.0 ... Chrome/130.0.0.0 Safari/537.36",
      "statusCode": 200,
      "responseTime": "1.00 ms",
      "resCookies": null
    }
  }
  • Loading branch information
ZL-Asica authored Nov 5, 2024
1 parent c68ab94 commit 3b87bfe
Showing 1 changed file with 30 additions and 25 deletions.
55 changes: 30 additions & 25 deletions src/middleware/worker-logger.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,58 @@
import type { Context, MiddlewareHandler } from 'hono'
import { getConnInfo } from 'hono/cloudflare-workers'

// Logging middleware dedicated for Cloudflare Workers
export const workerLogger: MiddlewareHandler = async (c: Context, next) => {
const reqHeaders = Object.fromEntries(Object.entries(c.req.header()))
const startTime = performance.now()
const info = getConnInfo(c)
const cfData = c.req.raw.cf || {}

const logData: Record<string, unknown> = {
method: c.req.method,
url: c.req.url,
host: reqHeaders.host,
platform: reqHeaders['sec-ch-ua-platform'],
userAgent: reqHeaders['user-agent'],
}

// log requestCookies only if it exists
if (reqHeaders.cookie) {
// Extract cookie names
logData.requestCookies = reqHeaders.cookie
?.split(';')
.map((c) => c.split('=')[0])
transport: info.remote.transport || null,
address: info.remote.address || null,
port: info.remote.port || null,
addressType: info.remote.addressType || null,
location:
`${cfData.city || ''}, ${cfData.region || ''}, ${cfData.country || ''}`.replace(
/(^,)|(,$)/g,
''
) || null,
timezone: cfData.timezone || null,
reqCookies:
c.req
.header('cookie')
?.split(';')
.map((c) => c.split('=')[0].trim()) || null,
platform: c.req.header('sec-ch-ua-platform')?.replace(/"/g, '') || null,
UA: c.req.header('User-Agent') || null,
}

try {
await next()

const responseTime = (performance.now() - startTime).toFixed(2)
const resHeaders = Object.fromEntries(c.res.headers)
const statusCode = c.res.status
const responseTime = `${(performance.now() - startTime).toFixed(2)} ms`
const statusCode = c.res.status || null

// Validate status code
if (!statusCode || typeof statusCode !== 'number' || isNaN(statusCode)) {
if (!statusCode) {
throw new Error('Invalid response status code')
}

// Skip logging for 404 responses
if (statusCode !== 404) {
logData.responseTime = `${responseTime} ms`
logData.statusCode = statusCode
if ('set-cookie' in resHeaders) {
logData.responseCookies = resHeaders['set-cookie']
}
logData.responseTime = responseTime
logData.resCookies = c.res.headers.get('set-cookie') || null

// Extract `error` field if status code indicates an error
if (statusCode >= 400) {
try {
const responseBody = await c.res.json<{ error: string }>()
logData.errorMessage = responseBody.error
logData.errorMessage = responseBody?.error || 'Unknown error'
} catch {
logData.errorMessage = 'Failed to parse error message'
logData.errorMessage = 'Unknown error'
}
}

Expand All @@ -56,17 +61,17 @@ export const workerLogger: MiddlewareHandler = async (c: Context, next) => {
console[level](
JSON.stringify({
level,
message: `Request processed - ${c.req.method} ${c.req.url}`,
message: `[${statusCode}] - [${c.req.method}] ${c.req.url}`,
details: logData,
})
)
}
} catch (error) {
logData.errorMessage = (error as Error).message
logData.errorMessage = (error as Error).message || 'Unknown error'
console.error(
JSON.stringify({
level: 'error',
message: `Request failed - ${c.req.method} ${c.req.url}`,
message: `[${logData.statusCode || '500'}] - [${c.req.method}] ${c.req.url} - ${logData.errorMessage}`,
details: logData,
})
)
Expand Down

0 comments on commit 3b87bfe

Please sign in to comment.