| 1 | --- |
| 2 | name: analyze-logs |
| 3 | description: Analyze application logs from the .evlog/logs/ directory. Use when debugging errors, investigating slow requests, understanding request patterns, or answering questions about application behavior. Reads structured NDJSON wide events written by evlog's file system drain. |
| 4 | license: MIT |
| 5 | metadata: |
| 6 | author: HugoRCD |
| 7 | version: "0.1" |
| 8 | --- |
| 9 | |
| 10 | # Analyze application logs |
| 11 | |
| 12 | Read and analyze structured wide-event logs from the local `.evlog/logs/` directory to debug errors, investigate performance issues, and understand application behavior. |
| 13 | |
| 14 | ## When to Use |
| 15 | |
| 16 | - User asks to debug an error, investigate a bug, or understand why something failed |
| 17 | - User asks about request patterns, slow endpoints, or error rates |
| 18 | - User asks "what happened" or "what's going on" with their application |
| 19 | - User asks to analyze logs, check recent errors, or review application behavior |
| 20 | - User mentions a specific error message or status code they're seeing |
| 21 | |
| 22 | ## Finding the logs |
| 23 | |
| 24 | Logs are written by evlog's file system drain as `.jsonl` files, organized by date. |
| 25 | |
| 26 | **Format detection**: The drain supports two modes: |
| 27 | |
| 28 | - **NDJSON** (default, |
| 29 | - **Pretty** ( |
| 30 | |
| 31 | Always check the first few bytes of the file to detect the format: if the second character is a newline or `"`, it's NDJSON; if it's a space or newline followed by spaces, it's pretty-printed. |
| 32 | |
| 33 | **Search order** — check these locations relative to the project root: |
| 34 | |
| 35 | 1. `.evlog/logs/` (default) |
| 36 | 2. Any `.evlog/logs/` inside app directories (monorepos: `apps/*/.evlog/logs/`) |
| 37 | |
| 38 | Use glob to find log files: |
| 39 | |
| 40 | ``` |
| 41 | .evlog/logs/*.jsonl |
| 42 | */.evlog/logs/*.jsonl |
| 43 | apps/*/.evlog/logs/*.jsonl |
| 44 | ``` |
| 45 | |
| 46 | Files are named by date: `2026-03-14.jsonl`. Start with the most recent file. |
| 47 | |
| 48 | ## If no logs are found |
| 49 | |
| 50 | The file system drain may not be enabled. Guide the user to set it up: |
| 51 | |
| 52 | ```typescript |
| 53 | import { createFsDrain } |
| 54 | |
| 55 | // Nuxt / Nitro: server/plugins/evlog-drain.ts |
| 56 | export default defineNitroPlugin((nitroApp |
| 57 | nitroApp.hooks.hook |
| 58 | }); |
| 59 | |
| 60 | // Hono / Express / Elysia: pass in middleware options |
| 61 | app.use(evlog( |
| 62 | |
| 63 | // Fastify: pass in plugin options |
| 64 | await app.register( |
| 65 | |
| 66 | // NestJS: pass in module options |
| 67 | EvlogModule.forRoot({ drain |
| 68 | |
| 69 | // Standalone: pass to initLogger |
| 70 | initLogger({ drain: createFsDrain |
| 71 | ``` |
| 72 | |
| 73 | After setup, the user needs to trigger some requests to generate logs, then re-analyze. |
| 74 | |
| 75 | ## Log format |
| 76 | |
| 77 | Each line is a self-contained JSON object (wide event). Key fields: |
| 78 | |
| 79 | | Field | Type | Description | |
| 80 | | ---------------- | -------- | |
| 81 | | `timestamp` | `string |
| 82 | | `level` | `string |
| 83 | | `service` | `string |
| 84 | | `environment` | `string |
| 85 | | `method` | `string |
| 86 | | `path` | `string |
| 87 | | `status` | `number |
| 88 | | `duration` | `string |
| 89 | | `requestId` | `string |
| 90 | | `error` | `object |
| 91 | | `error.data.why` | `string |
| 92 | | `error.data.fix` | `string |
| 93 | | `source` | `string |
| 94 | | `userAgent` | `object |
| 95 | |
| 96 | All other fields are application-specific context added via `log.set()` (e.g. `user`, ` |
| 97 | |
| 98 | ## How to analyze |
| 99 | |
| 100 | ### Step 1: Read the most recent log file |
| 101 | |
| 102 | Read the latest `.jsonl` file. Each line is one JSON event. Parse each line independently. |
| 103 | |
| 104 | ### Step 2: Identify the relevant events |
| 105 | |
| 106 | Filter based on the user's question: |
| 107 | |
| 108 | - **Errors**: look for |
| 109 | - **Specific endpoint**: match on |
| 110 | - **Slow requests**: parse |
| 111 | - **Specific user/action**: match on application-specific fields |
| 112 | - **Client-side issues**: filter by |
| 113 | - **Time range**: compare |
| 114 | |
| 115 | ### Step 3: Analyze and explain |
| 116 | |
| 117 | For each relevant event: |
| 118 | |
| 119 | 1. **What happened**: summarize the |
| 120 | 2. **Why it failed** (errors): read |
| 121 | 3. **How to fix**: check |
| 122 | 4. **Context**: examine application-specific fields for business context (user info, payment details, etc.) |
| 123 | 5. **Patterns**: look for recurring errors, degrading performance, or correlated failures |
| 124 | |
| 125 | ## Analysis patterns |
| 126 | |
| 127 | ### Find all errors |
| 128 | |
| 129 | ``` |
| 130 | Filter: level === "error" |
| 131 | Group by: error.message or path |
| 132 | Look for: recurring patterns, common failure modes |
| 133 | ``` |
| 134 | |
| 135 | ### Find slow requests |
| 136 | |
| 137 | ``` |
| 138 | Filter: parse duration string, compare > threshold (e.g. 1000ms) |
| 139 | Sort by: duration descending |
| 140 | Look for: specific endpoints, time-of-day patterns |
| 141 | ``` |
| 142 | |
| 143 | ### Trace a specific request |
| 144 | |
| 145 | ``` |
| 146 | Filter: requestId === "the-request-id" |
| 147 | Result: single wide event with all context for that request |
| 148 | ``` |
| 149 | |
| 150 | ### Error rate by endpoint |
| 151 | |
| 152 | ``` |
| 153 | Group events by: path |
| 154 | Count: total events vs error events per path |
| 155 | Look for: endpoints with high error ratios |
| 156 | ``` |
| 157 | |
| 158 | ### Client vs server errors |
| 159 | |
| 160 | ``` |
| 161 | Split by: source === "client" vs no source field |
| 162 | Compare: error patterns between client and server |
| 163 | Look for: client errors that don't have corresponding server errors (network issues) |
| 164 | ``` |
| 165 | |
| 166 | ## Important notes |
| 167 | |
| 168 | - Each line is a **complete, self-contained event**. Unlike traditional logs, you don't need to correlate multiple lines — one line has all the context for one request. |
| 169 | - The `error.data.why` and `error.data.fix` fields are evlog-specific structured error fields. When present, they provide the most actionable information. |
| 170 | - Duration values are strings with units (e.g. `"706ms"`). Parse the numeric part for comparisons. |
| 171 | - Events with `"source":"client"` originated from browser-side logging and were sent to the server via the transport endpoint. |
| 172 | - Log files are `.gitignore`'d automatically — they exist only on the local machine or server where the app runs. |