| 1 | # Code Review Checklist |
| 2 | |
| 3 | Use this checklist when reviewing code for logging best practices and evlog adoption. |
| 4 | |
| 5 | ## Quick Scan |
| 6 | |
| 7 | Run through these checks first to identify improvement opportunities: |
| 8 | |
| 9 | ### 1. Console Statement Audit |
| 10 | |
| 11 | Search for these patterns: |
| 12 | |
| 13 | ```typescript |
| 14 | // ❌ Patterns to find and transform |
| 15 | console.log(...) |
| 16 | console.error(...) |
| 17 | console.warn(...) |
| 18 | console.info(...) |
| 19 | console.debug(...) |
| 20 | ``` |
| 21 | |
| 22 | **Questions to ask:** |
| 23 | |
| 24 | - Are there multiple console statements in one function? |
| 25 | - Are they logging request/response data? |
| 26 | - Could they be consolidated into a wide event? |
| 27 | |
| 28 | ### 2. Error Pattern Audit |
| 29 | |
| 30 | Search for these patterns: |
| 31 | |
| 32 | ```typescript |
| 33 | // ❌ Generic errors |
| 34 | throw new Error(' |
| 35 | throw Error('...') |
| 36 | |
| 37 | // ❌ Re-throwing without context |
| 38 | catch (error) { |
| 39 | throw error |
| 40 | } |
| 41 | |
| 42 | // ❌ Logging and throwing |
| 43 | catch (error) { |
| 44 | console.error(error |
| 45 | throw error |
| 46 | } |
| 47 | ``` |
| 48 | |
| 49 | **Questions to ask:** |
| 50 | |
| 51 | - Does the error message explain what happened? |
| 52 | - Is there a `why` explaining the root cause? |
| 53 | - Is there a `fix` suggesting a solution? |
| 54 | - Is the original error preserved as `cause`? |
| 55 | |
| 56 | ### 3. Request Handler Audit |
| 57 | |
| 58 | For each API route/handler, check: |
| 59 | |
| 60 | ```typescript |
| 61 | // ❌ Missing request context |
| 62 | export default defineEventHandler(async |
| 63 | // No logging at all, or scattered console.logs |
| 64 | }); |
| 65 | ``` |
| 66 | |
| 67 | **Questions to ask:** |
| 68 | |
| 69 | - Is there a request-scoped logger? |
| 70 | - Is context accumulated throughout the request? |
| 71 | - Is there a single emit at the end? |
| 72 | |
| 73 | ## Detailed Review |
| 74 | |
| 75 | ### Console.log Transformations |
| 76 | |
| 77 | #### Single Debug Log |
| 78 | |
| 79 | ```typescript |
| 80 | // ❌ Before |
| 81 | console.log("Processing user:" |
| 82 | |
| 83 | // ✅ After - if part of a larger operation |
| 84 | log.set({ user |
| 85 | |
| 86 | // ✅ After - if standalone debug |
| 87 | log.debug("user" |
| 88 | ``` |
| 89 | |
| 90 | #### Multiple Related Logs |
| 91 | |
| 92 | ```typescript |
| 93 | // ❌ Before |
| 94 | console.log("Starting checkout" |
| 95 | console.log("User:" |
| 96 | console.log("Cart items:" |
| 97 | console.log("Total:" |
| 98 | |
| 99 | // ✅ After |
| 100 | log.info({ |
| 101 | action: "checkout", |
| 102 | user: { id: user |
| 103 | cart: { items: cart |
| 104 | }); |
| 105 | ``` |
| 106 | |
| 107 | #### Request Lifecycle Logs |
| 108 | |
| 109 | ```typescript |
| 110 | // server/api/process.post.ts |
| 111 | |
| 112 | // ❌ Before |
| 113 | export default defineEventHandler(async |
| 114 | console.log("Request started |
| 115 | const user = |
| 116 | console.log("User loaded |
| 117 | const result = |
| 118 | console.log("Processing complete |
| 119 | return result; |
| 120 | }); |
| 121 | |
| 122 | // ✅ After (Nuxt - auto-imported, no import needed) |
| 123 | // For Nitro v3: import { useLogger } from 'evlog/nitro/v3' |
| 124 | // For Nitro v2: import { useLogger } from 'evlog/nitro' |
| 125 | |
| 126 | export default defineEventHandler(async |
| 127 | const log = |
| 128 | |
| 129 | const user = |
| 130 | log.set({ |
| 131 | |
| 132 | const result = |
| 133 | log.set({ |
| 134 | |
| 135 | return result; |
| 136 | // emit() called automatically |
| 137 | }); |
| 138 | ``` |
| 139 | |
| 140 | ### Error Transformations |
| 141 | |
| 142 | #### Generic Error |
| 143 | |
| 144 | ```typescript |
| 145 | // ❌ Before |
| 146 | throw new Error(" |
| 147 | |
| 148 | // ✅ After |
| 149 | throw createError({ |
| 150 | message: "Failed to create user", |
| 151 | why: "Email address already registered", |
| 152 | fix: "Use a different email or log in to existing account", |
| 153 | link: "https://your-app.com/docs/registration", |
| 154 | }); |
| 155 | ``` |
| 156 | |
| 157 | #### Wrapped Error Without Context |
| 158 | |
| 159 | ```typescript |
| 160 | // ❌ Before |
| 161 | try { |
| 162 | await externalApi.call |
| 163 | } catch (error) |
| 164 | throw new Error( |
| 165 | } |
| 166 | |
| 167 | // ✅ After |
| 168 | try { |
| 169 | await externalApi.call |
| 170 | } catch (error) |
| 171 | throw createError({ |
| 172 | message: "External API call failed", |
| 173 | why: `API returned: ${error |
| 174 | fix: "Check API credentials and try again", |
| 175 | link: "https://api-docs.example.com/errors", |
| 176 | cause: error, |
| 177 | }); |
| 178 | } |
| 179 | ``` |
| 180 | |
| 181 | #### Log-and-Throw Anti-pattern |
| 182 | |
| 183 | ```typescript |
| 184 | // ❌ Before |
| 185 | try { |
| 186 | await riskyOperation(); |
| 187 | } catch (error) |
| 188 | console.error("Operation failed: |
| 189 | throw error; |
| 190 | } |
| 191 | |
| 192 | // ✅ After |
| 193 | try { |
| 194 | await riskyOperation(); |
| 195 | } catch (error) |
| 196 | log.error(error |
| 197 | throw createError({ |
| 198 | message: "Operation failed", |
| 199 | why: error.message |
| 200 | fix: "Check input and retry", |
| 201 | cause: error, |
| 202 | }); |
| 203 | } |
| 204 | ``` |
| 205 | |
| 206 | ### Request Handler Transformations |
| 207 | |
| 208 | #### No Logging |
| 209 | |
| 210 | ```typescript |
| 211 | // server/api/orders.post.ts |
| 212 | |
| 213 | // ❌ Before |
| 214 | export default defineEventHandler(async |
| 215 | const body = |
| 216 | const result = |
| 217 | return result; |
| 218 | }); |
| 219 | |
| 220 | // ✅ After (Nuxt - auto-imported, no import needed) |
| 221 | // For Nitro v3: import { useLogger } from 'evlog/nitro/v3' |
| 222 | // For Nitro v2: import { useLogger } from 'evlog/nitro' |
| 223 | import { createError } |
| 224 | |
| 225 | export default defineEventHandler(async |
| 226 | const log = |
| 227 | |
| 228 | const body = |
| 229 | log.set({ |
| 230 | |
| 231 | try { |
| 232 | const result = |
| 233 | log.set({ |
| 234 | return result; |
| 235 | } catch (error) |
| 236 | log.error(error |
| 237 | throw createError({ |
| 238 | message: "Order processing failed", |
| 239 | why: error.message |
| 240 | fix: "Check the order data and try again", |
| 241 | }); |
| 242 | } |
| 243 | // emit() called automatically |
| 244 | }); |
| 245 | ``` |
| 246 | |
| 247 | ## Review Checklist Summary |
| 248 | |
| 249 | ### Logging |
| 250 | |
| 251 | - [ ] No raw `console.log` statements in production code |
| 252 | - [ ] Request handlers use `useLogger(event)` (Nuxt/Nitro) or `createRequestLogger()` (standalone) |
| 253 | - [ ] Context is accumulated with `log.set()` throughout the request |
| 254 | - [ ] `emit()` is automatic with `useLogger()`, manual with |
| 255 | - [ ] Wide events include: user, business context, outcome |
| 256 | |
| 257 | ### Errors |
| 258 | |
| 259 | - [ ] All errors use `createError()` instead of `new Error()` (import from |
| 260 | - [ ] Every error has a clear `message` and appropriate `status` code |
| 261 | - [ ] Complex errors include `why` explaining root cause |
| 262 | - [ ] Fixable errors include `fix` with actionable steps |
| 263 | - [ ] Documented errors include `link` to docs |
| 264 | - [ ] Wrapped errors preserve `cause` |
| 265 | - [ ] Support-only or sensitive diagnostics use `internal`, not `message` / |
| 266 | |
| 267 | ### Frontend Error Handling |
| 268 | |
| 269 | - [ ] API errors are caught and displayed with full context (message, why, fix) |
| 270 | - [ ] Toasts or error components use the structured data from `error.data.data` |
| 271 | - [ ] Links to documentation are actionable (buttons/links in toasts) |
| 272 | |
| 273 | ### Context |
| 274 | |
| 275 | - [ ] User context includes: id, plan/subscription, relevant business data |
| 276 | - [ ] Request context includes: method, path, requestId |
| 277 | - [ ] Business context is domain-specific and useful for debugging |
| 278 | - [ ] No sensitive data in logs (passwords, tokens, full card numbers) |
| 279 | |
| 280 | ## Anti-Pattern Summary |
| 281 | |
| 282 | | Anti-Pattern | Fix | |
| 283 | | -------------------------------------- | --------------------------------------------------------------------------- | |
| 284 | | Multiple `console.log` in one function | Single wide event with `useLogger(event).set() |
| 285 | | `throw new Error('...')` | `throw createError({ message, status, why, fix }) |
| 286 | | `console.error(e); throw e` | `log.error(e); throw createError(...) |
| 287 | | No logging in request handlers | Add `useLogger(event)` (Nuxt/Nitro) or `createRequestLogger() |
| 288 | | Flat log data | Grouped objects: `{ user: {...}, cart: {...} }` | |
| 289 | | Abbreviated field names | Descriptive names: `userId` not `uid |
| 290 | |
| 291 | ## Suggested Review Comments |
| 292 | |
| 293 | Use these when leaving review feedback: |
| 294 | |
| 295 | ### Console.log Found |
| 296 | |
| 297 | > Consider using evlog's wide event pattern here. Instead of multiple console.log statements, use `useLogger(event) |
| 298 | |
| 299 | ### Generic Error |
| 300 | |
| 301 | > This error would benefit from evlog's structured error pattern. Consider using `import { createError } from 'evlog' |
| 302 | |
| 303 | ### Missing Request Context |
| 304 | |
| 305 | > This handler would benefit from request-scoped logging. Add `useLogger(event) |
| 306 | |
| 307 | ### Good Logging (Positive Feedback) |
| 308 | |
| 309 | > Nice use of wide events here! The context is well-structured and will be very useful for debugging. |